Files
ipam_control/docs/changes/029-prefix-tree-lock/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

2.8 KiB
Raw Blame History

Целостность дерева префиксов при параллельных изменениях (изменение 029)

Находка № 5 из docs/reviews/2026-09-26-codebase-review-2.md, серьёзность — низкая.

Context

attach_to_tree определяет родителя и забирает вложенные префиксы по снимку данных транзакции. Два параллельных запроса могут создать в одном VRF пересекающиеся префиксы разной длины (ручное создание .0/29 и автовыделение .4/30). Уникальность (vrf_id, prefix) это не ловит, parent_id останется неверным. FOR UPDATE в allocate_subnet блокирует только строку родителя.

Решение (app/api/v1/prefixes.py)

  1. Хелпер _lock_vrf(db, *vrf_ids): pg_advisory_xact_lock(<VRF_NS>, vrf_id) для каждого VRF в порядке возрастания id (исключает взаимную блокировку); пространство ключей — константа (например, 7033).
  2. Вызов в начале каждой операции, меняющей дерево или раскладку адресов, до любых чтений дерева:
    • create_prefix — VRF из тела запроса;
    • allocate_subnet — VRF родителя. Сначала прочитать vrf_id родителя обычным SELECT, затем _lock_vrf, затем уже существующий SELECT … FOR UPDATE;
    • update_prefix при смене VRF (_move_to_vrf) — исходный и целевой VRF;
    • delete_prefix — VRF префикса (переподвешивание детей);
    • create_address и allocate_next — VRF префикса (выбор самого узкого префикса и занятых диапазонов должен видеть согласованное дерево).
  3. Операции над разными VRF не блокируют друг друга. Чтения (GET) не блокируются.
  4. README.md: одна фраза в разделе «Модель данных» — изменения дерева одного VRF выполняются по одному.

Файлы

app/api/v1/prefixes.py, README.md.

Проверка

Параллельно (потоки) в одном VRF: создание .0/29 и автовыделение /30 из /24 → у выделенного /30, если он внутри /29, родитель — /29. Цикл из 20 повторов без ошибок и без неверных родителей. Данные после проверки удалить.