44 lines
5.3 KiB
Markdown
44 lines
5.3 KiB
Markdown
# Исправление замечаний 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`.
|