Files
ipam_control/docs/changes/030-review-fixes-025-029/PLAN.md
T
ayurishchevandClaude Opus 5.5 03d727e496 Задачи 025-030: ёмкость префиксов, политика входа, дерево префиксов
Повторный анализ кодовой базы (docs/reviews/2026-09-26-codebase-review-2.md) и доработки:
025 Ёмкость префикса — размер его подсети (а не сумма листьев); «Обзор» считает ёмкость
    по корневым активным IPv4-префиксам и адреса внутри них.
026 Политика блокировки входа: 5 неудач на логин+IP, 20 на IP, 50 на логин со всех IP,
    кроме известных IP (known_logins, миграция 0009) — владельца нельзя заблокировать анонимно.
027 Сериализация попыток входа по IP (advisory-lock после блокировки логина).
028 UI «Префиксы»: загрузка всех страниц (до 20 000), счётчики по total, предупреждение об усечении.
029 Advisory-lock по VRF для операций, меняющих дерево префиксов и раскладку адресов.
030 Исправление замечаний ревью 025-029: _lock_prefix (VRF блокируется до чтения префикса,
    409 при одновременном переносе), константы политики входа перенесены в app/services.py.

Тесты: 14 passed (проверка ёмкости родителя приведена к семантике 025); сквозные сценарии
и гонки — docs/reviews/2026-09-26-changes-025-029-review.md, 2026-09-27-changes-030-review.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:28:50 +03:00

5.3 KiB
Raw Blame History

Исправление замечаний 1–2 ревью изменений 025–029 (изменение 030)

Источник: docs/reviews/2026-09-26-changes-025-029-review.md, замечания № 1 и № 2. Только код. Тесты исполнитель не пишет и не запускает (тестирование — отдельный шаг).

1. Чтение префикса до блокировки VRF (низкая, 029)

Где: app/api/v1/prefixes.py — delete_prefix, update_prefix (при смене VRF), create_address. Суть: префикс читается через get_or_404 до _lock_vrf. Возникают две проблемы:

  • в delete_prefix устаревший p.parent_id используется для переподвешивания детей;
  • если префикс параллельно переносят в другой VRF, блокируется не тот VRF (p.vrf_id прочитан до блокировки).

Решение:

  1. Хелпер _lock_prefix(db, id: int, *extra_vrf_ids: int) -> Prefix рядом с _lock_vrf:
    • прочитать vrf_id префикса отдельным SELECT Prefix.vrf_id; нет строки — 404 «Префикс не найден»;
    • _lock_vrf(db, vrf_id, *extra_vrf_ids) — все VRF одним вызовом, порядок по возрастанию id уже обеспечен _lock_vrf;
    • перечитать префикс под блокировкой: select(Prefix).where(Prefix.id == id).with_for_update().execution_options(populate_existing=True), чтобы не взять устаревший объект из identity map сессии;
    • если перечитанный vrf_id отличается от заблокированного (префикс успели перенести), повторить цикл, не больше 3 попыток, затем 409 «Префикс одновременно изменяется, повторите запрос». Блокировки транзакции при повторе не снимаются, это допустимо: порядок захвата внутри одной попытки по-прежнему возрастающий. Если «лишняя» блокировка ухудшает порядок между попытками, допустимо вместо повтора сразу возвращать 409. Выбрать вариант и указать его в SUMMARY.
  2. Применение:
    • delete_prefix: p = _lock_prefix(db, id) вместо get_or_404 + _lock_vrf;
    • create_address: то же;
    • update_prefix: целевой VRF (body.vrf_id) известен из тела запроса до чтения префикса, поэтому p = _lock_prefix(db, id, new_vrf) при заданном vrf_id, иначе обычный get_or_404 (без смены VRF дерево не меняется). Если new_vrf совпал с текущим p.vrf_id, лишняя блокировка того же VRF безвредна.
    • allocate_subnet и allocate_next уже читают vrf_id до блокировки; перевести их на _lock_prefix для единообразия, сохранив поведение (FOR UPDATE строки префикса есть в хелпере).
  3. Комментарии — кратко, со ссылкой на изменение 030.

2. Зависимость ротации от API-слоя (низкая, 026)

Где: app/rotation.py импортирует KNOWN_IP_DAYS из app.api.v1.auth. Решение:

  1. Перенести константы политики входа — LOGIN_WINDOW (сейчас WINDOW), MAX_PER_LOGIN_IP, MAX_PER_IP, MAX_PER_LOGIN, KNOWN_IP_DAYS — в app/services.py отдельным блоком с комментарием.
  2. app/api/v1/auth.py импортирует их из app.services; внутренние имена в auth.py не должны расходиться с перенесёнными. WINDOW заменить на LOGIN_WINDOW по месту, модульная docstring с .format(...) продолжает работать.
  3. app/rotation.py импортирует KNOWN_IP_DAYS из app.services; импорт app.api.v1.auth удалить.
  4. Пространства ключей advisory-lock (LOGIN_LOCK_NS, IP_LOCK_NS) остаются в auth.py.

Артефакты

  • docs/changes/030-review-fixes-025-029/SUMMARY.md: что сделано, выбранный вариант повтора из п. 1, отклонения.
  • README.md — только если меняется описанное поведение (ожидается, что не меняется).

Проверка (выполняет ревьюер, не исполнитель)

  • Импорт приложения; rotation.py не импортирует API-модули.
  • Параллельные сценарии по п. 1: удаление префикса ∥ вставка промежуточного префикса; перенос префикса в другой VRF ∥ создание адреса / удаление.
  • Регрессия: pytest -q.