30 lines
3.2 KiB
Markdown
30 lines
3.2 KiB
Markdown
# Экран адресов: ресурсоёмкие ветки list_addresses (изменение 014)
|
||||
|
|
|
|||
|
|
Находка ревью № 4, серьёзность — средняя (DoS с ролью viewer).
|
|||
|
|
|
|||
|
|
## Context
|
|||
|
|
`GET /prefixes/{id}/addresses` (`app/api/v1/prefixes.py`, `list_addresses`):
|
|||
|
|
- при `status=free` собирает в память `offset + limit` свободных адресов; `offset` не ограничен → на IPv6-префиксе один запрос с `offset=10^8` занимает CPU и память воркера;
|
|||
|
|
- без фильтров основной запрос идёт **без LIMIT/OFFSET** — на крупных подсетях загружаются все адреса ради одной страницы.
|
|||
|
|
|
|||
|
|
## Решение
|
|||
|
|
1. **Свободные адреса без материализации:** функция `free_page(net, occupied_sorted, offset, limit)` в `app/services.py` — идёт по занятым диапазонам,
|
|||
|
|
считает длины свободных промежутков арифметически, пропускает `offset` без перебора и возвращает только `limit` адресов. Для IPv4 ≤ /30 учитывает исключение адреса сети и broadcast.
|
|||
|
|
Занятые адреса — одним запросом `SELECT address … ORDER BY address` (для пула из 10⁵ адресов — приемлемо; при росте — курсор по ключу).
|
|||
|
|
2. **Режимы:**
|
|||
|
|
- `status` = assigned/reserved/deprecated или поиск `q` → пагинация в SQL (`ORDER BY address LIMIT/OFFSET`, `total` — `count()`);
|
|||
|
|
- без фильтров и `cap <= FREE_LISTING_LIMIT` (4096) → как сейчас, смешанный список (он ограничен размером подсети);
|
|||
|
|
- без фильтров и `cap > FREE_LISTING_LIMIT` → только записанные адреса, пагинация в SQL (сейчас — выборка всего и срез в Python);
|
|||
|
|
- `status=free` → `free_page`, `total = summary.free`.
|
|||
|
|
3. `offset` — верхний предел как в № 013; для `status=free` дополнительно `offset <= summary.free`.
|
|||
|
|
4. Ответ API и поведение UI (кнопка «Показать ещё 100») не меняются.
|
|||
|
|
|
|||
|
|
## Файлы
|
|||
|
|
`app/services.py`, `app/api/v1/prefixes.py`, `tests/test_api.py`, `README.md`.
|
|||
|
|
|
|||
|
|
## Тест
|
|||
|
|
- Чистая функция `free_page`: IPv4 /24 с занятыми `.1–.3` → offset 0 даёт `.4…`; offset за пределами → пусто; IPv6 /64 с `offset=10^12` — мгновенно.
|
|||
|
|
- API: `status=free&offset=1000000` на IPv6 /64 отвечает быстро (порог времени в тесте не ставим — проверяем корректность адресов).
|
|||
|
|
|
|||
|
|
## Проверка
|
|||
|
|
`pytest -q`; экран адресов демо-префиксов `10.10.1.0/24` и `fd00::/8` в UI: списки, фильтр «Свободен», «Показать ещё 100». Замер времени ответа до/после на `/16` с 60 000 адресов (скрипт в scratchpad).
|