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>
36 lines
6.1 KiB
Markdown
36 lines
6.1 KiB
Markdown
# План: защита от пустой выдачи после потери базы (находка 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` можно удалить.
|