Files
ripe-cidr-collector/docs/plan-loss-guard.md
T
ayurishchevandClaude Sonnet 5 0155fd3f38 Withhold addresses after the database is recreated without a backup
When ripe.db is corrupted and no valid backup exists, a new empty database
is created with a db_recreated.json marker; /addresses and /addresses/diff
answer 503 until the collector gathers data again, /health reports
db_recreated. Tests now isolate all state files via conftest.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 09:56:43 +03:00

36 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: защита от пустой выдачи после потери базы (находка 1 ревью)
Источник: `docs/review-2026-09-21.md`, находка 1 (высокая); риск 3 анализа.
## Проблема
Если `ripe.db` испорчена и исправной копии нет, первый запрос получает 503, а затем создаётся новая пустая база, и `/addresses` отвечает `200 []`. Потребитель (роутер, файрвол), забирающий список по расписанию, может принять пустой список за истину и стереть свои правила. `/health` при живом демоне показывает `ok` при `counts = 0`.
## Дизайн
- **Признак потери.** Когда база пересоздана из-за порчи без копий, `db._recover` пишет файл-метку `db_recreated.json` в `DATA_DIR` (время, путь карантина, текст ошибки). Метка ставится только в этом случае: чистая установка (базы ещё не было) и восстановление из копии её не создают.
- **Сразу рабочее соединение.** `_recover` больше не бросает `StorageError` в этом случае, а создаёт новую базу и возвращает соединение (демон продолжает сбор, `/health` и записи работают). Журнал изменений новой базы сдвигается так же, как при восстановлении из копии (`RESTORE_JOURNAL_JUMP`): курсоры старой базы дают `410`. Общий код сброса журнала выносится из `_restore_backup`.
- **Что блокируется.** Пока данные не собраны заново, `GET /addresses` и `GET /addresses/diff` отвечают `503` с заголовком `Retry-After` и пояснением; остальные эндпоинты работают. Проверка идёт по запрошенным типам:
- блокируется тип, для которого в `config.json` есть источники, но в базе по нему нет ни одного значения;
- тип без источников (например, нет ни одного FQDN) не блокируется и отдаёт законный пустой список.
- **Снятие метки.** В конце каждого запуска сбора (`ASN`, `FQDN`, в том числе через CLI) вызывается `db.settle_recreated`: если ни один сконфигурированный тип не пуст, файл-метка удаляется. Снять блокировку вручную (принять пустую выдачу) можно, удалив `db_recreated.json`.
- **`/health`.** Поле `db_recreated`: `null` или `{"at": ..., "pending": true|false}` (без путей: это же закрывает связанную находку 6 для нового поля). Пока блокировка активна, статус `degraded`.
- **Устойчивость.** Проверка метки читает только файл и базу; если `config.json` нечитаем, блокировка сохраняется (fail closed).
## Изменения
1. `cidr_collector.py`: `RECREATED_FILE`, вызов `db.settle_recreated` в обоих `run_collection`.
2. `db.py`: метка и создание новой базы в `_recover`, `_reset_journal` (общий код с `_restore_backup`), `recreated_pending(conn, kinds)`, `settle_recreated(conn)`.
3. `api_server.py`: проверка в `/addresses` и `/addresses/diff` (503 + `Retry-After`), поле `db_recreated` и статус в `/health`.
4. `README.md`: раздел «Automatic restore» (поведение без копий, ручное снятие метки), `/health`.
5. `.gitignore`/`.dockerignore`: `db_recreated.json`.
6. Тесты (2 новых, один существующий обновляется; всего 24): БД (порча без копий: соединение рабочее, метка записана, старый курсор `410`; метка снимается при появлении данных и не мешает, если источников нет); API (`/addresses` и `/addresses/diff` дают 503, `/health` `degraded` с `db_recreated`, после появления данных 200 и метка снята; тип без источников не блокируется). Существующая проверка «без копий -> StorageError» в `test_restore_from_backup` заменяется на новое поведение.
## Не входит
- Чистая установка без базы по-прежнему отдаёт пустой список (терять нечего).
- **Удаление файла базы вручную не распознаётся** как потеря (порчи нет): пустая база создаётся без метки. Возможное продолжение: при отсутствии базы и наличии копий восстанавливаться из них.
- Порча внутри файла, обнаруженная при чтении, по-прежнему даёт 503 и восстанавливается вручную.
## Проверка
Тесты в контейнере; вручную в отдельных процессах: порча `ripe.db` без копий при работающих API и демоне -> `/addresses` 503, `/health` `degraded`, после сбора 200 с данными и метка удалена; повтор в Docker Compose (общий том).
## Откат
Убрать проверку в API и запись метки в `_recover` (возврат к предыдущему коммиту): схема базы не меняется, файл `db_recreated.json` можно удалить.