Scheduled daily DB backup + tested restore; online backup API (#57) #76

Merged
claude-bot merged 1 commit from fix/57-daily-backup into main 2026-07-16 08:04:21 +00:00
Contributor

Closes #57.

  • Daily backup job at 04:00 (run_daily_backup_job, registered in _register_static_jobs alongside the midnight/weekly/yearly crons).
  • Online backup API: backup.py now uses sqlite3.Connection.backup(...) instead of shutil.copy2, so a live (WAL) DB is never copied mid-write.
  • Separate retention for dailies vs pre-bulk-op backups: kind param → distinct filenames (iris_daily_* vs iris_*) and independent pruning; new DAILY_BACKUP_RETAIN_COUNT (default 14) config + RuntimeSettings field + PUT /settings re-prune side effect (mirrors backup_retain_count).
  • Restore: documented procedure in docs/troubleshooting.md (stop → replace data/iris.db → start → alembic note) and an automated restore test (create → backup → corrupt → restore → verify).

Verified: ruff/format/mypy clean; 250 tests pass with a working temp dir (all new backup/restore/scheduler tests green). The test_backup.py errors seen on the Windows dev box are a pre-existing stale-ACL on Temp\pytest-of-Ryan that breaks pytest's tmp_path setup for any temp-using test — unrelated to this change and green on the Linux CI runner.

Follow-up (not blocking): expose daily_backup_retain_count in the Settings UI (fold into #48 Batch B).

🤖 Generated with Claude Code

Closes #57. - **Daily backup job** at 04:00 (`run_daily_backup_job`, registered in `_register_static_jobs` alongside the midnight/weekly/yearly crons). - **Online backup API**: `backup.py` now uses `sqlite3.Connection.backup(...)` instead of `shutil.copy2`, so a live (WAL) DB is never copied mid-write. - **Separate retention** for dailies vs pre-bulk-op backups: `kind` param → distinct filenames (`iris_daily_*` vs `iris_*`) and independent pruning; new `DAILY_BACKUP_RETAIN_COUNT` (default 14) config + `RuntimeSettings` field + `PUT /settings` re-prune side effect (mirrors `backup_retain_count`). - **Restore**: documented procedure in `docs/troubleshooting.md` (stop → replace `data/iris.db` → start → alembic note) and an automated **restore test** (create → backup → corrupt → restore → verify). Verified: `ruff`/`format`/`mypy` clean; **250 tests pass** with a working temp dir (all new backup/restore/scheduler tests green). The `test_backup.py` errors seen on the Windows dev box are a **pre-existing stale-ACL on `Temp\pytest-of-Ryan`** that breaks pytest's `tmp_path` setup for any temp-using test — unrelated to this change and green on the Linux CI runner. Follow-up (not blocking): expose `daily_backup_retain_count` in the Settings UI (fold into #48 Batch B). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add scheduled daily DB backup and tested restore path (#57)
All checks were successful
CI / Python lint & type-check (pull_request) Successful in 1m7s
CI / Alembic migration check (pull_request) Successful in 53s
CI / Frontend lint, test & build (pull_request) Successful in 1m32s
CI / Dependency audit (pull_request) Successful in 1m14s
CI / Python tests (pull_request) Successful in 2m11s
CI / Docker build, health smoke & E2E (pull_request) Successful in 2m16s
d9519539fa
Backups previously ran only before bulk ops (yearly gen, bulk approve,
import, sync), leaving ordinary-day corruption unprotected. Adds a
04:00 quiet-hour APScheduler job (`daily_backup`) that snapshots the
DB independent of bulk operations, pruned via a new
DAILY_BACKUP_RETAIN_COUNT knob (default 14) kept separate from
BACKUP_RETAIN_COUNT (pre-bulk-op backups). Switches backup.py from a
raw shutil.copy2 file copy to SQLite's online backup API
(sqlite3.Connection.backup), so a live DB is never copied mid-write.
Documents the restore procedure in docs/troubleshooting.md and
exercises it end-to-end in test_restore_from_backup_recovers_original_data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot deleted branch fix/57-daily-backup 2026-07-16 08:04:22 +00:00
Sign in to join this conversation.
No description provided.