Files
ipam_control/docs/changes/030-review-fixes-025-029/PLAN.md
T

44 lines
5.3 KiB
Markdown
Raw Normal View 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`.