Files
ayurishchevandClaude Sonnet 5 02f7b49e12 Add database backups and automatic restore
Daemon job "backup" (online copy, quick_check, rotation), restore of the
newest valid copy when the database cannot be opened, change journal reset
after restore, last_restore in /health, docs and tests.

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

4.8 KiB

Итоги: резервные копии базы и автовосстановление (доработка Б)

План: docs/plan-db-backup.md.

Сделано

  • Задание backup в демоне (collector_daemon.py): третье задание рядом с asn и fqdn (то же расписание, защита от наложения, состояние в status.json); по умолчанию 30 4 * * *, меняется в schedule.backup или через POST /schedule (type: backup). Реестр COLLECTORS теперь хранит вызываемые объекты, существующие тесты не менялись.
  • Копия (db.backup_database): онлайн-копия средствами SQLite, quick_check живой базы и копии, запись через *.tmp и переименование, ротация (backup_keep, по умолчанию 7) только после успешной новой. Если проверка не пройдена, копия не создаётся, старые остаются, /health показывает degraded.
  • Автовосстановление (db.connect -> _recover): порченая база уходит в *.corrupt-*, на её место встаёт самая свежая копия, прошедшая проверку (битые копии пропускаются); под файловой блокировкой, с повторной проверкой внутри (гонка API и демона). Без исправных копий прежнее поведение (StorageError, 503).
  • Журнал diff после отката: журнал очищается, счётчик сдвигается на 1 000 000, горизонт на момент восстановления: все выданные ранее курсоры и времена дают 410.
  • Заметность: лог ERROR, файл last_restore.json, поле last_restore в /health.
  • Прочее: RIPE_BACKUP_DIR (по умолчанию <DATA_DIR>/backups), backups/ и last_restore.json в .gitignore/.dockerignore, POST /schedule принимает backup.
  • README: расписание и backup_keep, раздел «Backup and automatic restore» (вынос копий с тома, ручное восстановление), last_restore в /health.
  • Тесты: 2 новых (копия, ротация и проверка; восстановление с пропуском битой копии, сброс курсоров, работа после отката, случай без копий). Всего 20, в контейнере 20 passed.

Проверка

  • Отдельные процессы (демон с backup раз в минуту и backup_keep=2, API): скопировано и ротировано до двух файлов; после записи мусора в ripe.db API поднял базу из копии, /addresses вернул данные, /health показал last_restore, карантинный файл сохранён; новый курсор принимается (200).
  • Docker Compose (отдельный проект, порт 18000, стенд убран): расписание backup выставлено через POST /schedule, копия появилась в /data/backups, порча базы в томе привела к восстановлению, оба сервиса healthy.

Замечания

  • Копии по умолчанию лежат на том же томе, что и база: это защита от порчи файла, но не от потери тома.
  • Автоматически восстанавливается только порча, видимая при открытии; порча внутри файла даёт 503, её замечает задание backup, восстановление ручное (порядок в README).
  • Сдвиг журнала на 1 000 000 - эвристика: старый курсор мог бы совпасть с новой историей, только если между копиями накопится больше миллиона изменений.
  • Отказ 410 для старого курсора после восстановления подтверждён тестом; вручную в отдельных процессах эту проверку не повторял (команда не выполнилась из-за экранирования в оболочке).
  • Не сделано: копии вне сервера, шифрование, команда CLI restore, ручной запуск копии через POST /collect.