Restoring a backup with no data while sources are configured now sets the db_recreated.json marker (503 until data is collected), and the backup job skips copying an empty database in that state. Adds finding 11 to the review. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
29 lines
5.2 KiB
Markdown
29 lines
5.2 KiB
Markdown
# План: защита от восстановления из пустой копии (находка 11)
|
||
|
||
Источник: трассировка `_recover()` по графу знаний и проверка запуском (см. `docs/review-2026-09-21.md`, находка 11).
|
||
|
||
## Проблема
|
||
`_recover` ставит на место повреждённой базы самую свежую исправную копию, но не смотрит на её содержимое. Если копия пуста (ночное задание `backup` сработало до первого сбора или во время его сбоев), а источники в `config.json` настроены, то после порчи базы API отвечает `200 []` без метки: та же угроза, что в находке 1 (потребитель может принять пустой список за истину), только через восстановление. Подтверждено запуском: копия пустой базы -> порча -> `/addresses` даёт `200 []`, метки нет.
|
||
|
||
## Дизайн
|
||
- **Признак «пусто несмотря на источники»:** `db.empty_despite_sources(conn)`: в базе нет ни одного значения, а в `config.json` настроен хотя бы один ASN или FQDN. Сначала проверяется число значений (конфиг не читается, если данные есть); нечитаемый конфиг при пустой базе считается «пусто» (блокировка по умолчанию).
|
||
Критерий намеренно общий, а не по типам: копия, где есть ASN, но нет адресов FQDN (например, все адреса отфильтрованы как неглобальные), не должна вечно держать выдачу в ожидании.
|
||
- **Восстановление:** после успешного `_restore_backup` при `empty_despite_sources` пишется та же метка `db_recreated.json` (с полем `reason`). Дальше работает уже существующая защита: `/addresses` и `/addresses/diff` дают `503` с `Retry-After`, `/health` показывает `degraded` и `db_recreated`, сборщик снимает метку, когда данные собраны. Восстановление копии с данными метку не ставит.
|
||
- **Задание `backup`:** пустую базу при настроенных источниках не копирует: `backup_database` возвращает `None` (не ошибка: статус задания остаётся успешным, в лог пишется сообщение), ротация не выполняется, существующие копии не затрагиваются. Так пустые копии не вытесняют хорошие за `backup_keep` дней и не появляются вовсе. Пустая база без источников копируется как раньше.
|
||
|
||
## Изменения
|
||
1. `db.py`: `empty_despite_sources`; `recreated_pending` использует общий разбор конфигурации; метка ставится в `_recover` (общая функция записи метки для обеих веток); `backup_database` возвращает `None` для пустой базы при источниках.
|
||
2. `collector_daemon.py`: `run_backup` обрабатывает `None` (сообщение в лог).
|
||
3. `README.md`: разделы о резервной копии и автовосстановлении.
|
||
4. `docs/review-2026-09-21.md`: находка 11; итоги в `docs/summary-empty-backup-guard.md`.
|
||
5. Тесты (1 новый, всего 28): пустая база при настроенных источниках копию не создаёт, копия с данными восстанавливается без метки, пустая копия (снятая, когда источников не было) при появлении источников даёт метку и `503`-состояние.
|
||
|
||
## Не входит
|
||
Тип-специфичное ожидание при восстановлении (см. выше); копии вне сервера; удаление уже существующих пустых копий (при необходимости - вручную из `backups/`).
|
||
|
||
## Проверка
|
||
Тесты в контейнере; повтор запуска, воспроизводившего проблему: восстановление из пустой копии при настроенном источнике теперь даёт `503`, метка есть, после появления данных выдача возобновляется; копия с данными - без метки.
|
||
|
||
## Откат
|
||
Убрать проверку в `_recover` и `backup_database` (возврат к предыдущему коммиту); схема данных не затрагивается, метку можно удалить вручную.
|