Skip to content

fix(backup): deploy dumps no longer push out the nightly backups - #181

Merged
sinashamsizadeh merged 4 commits into
mainfrom
fix/backup-retention-pools
Oct 10, 2026
Merged

sinashamsizadeh merged 4 commits into
mainfrom
fix/backup-retention-pools

Conversation

@sinashamsizadeh

Copy link
Copy Markdown
Collaborator

Root cause

app/services/pg_backup.py prune(keep) kept the newest keep (default 14) backups of ANY reason. Pre-deploy dumps,
rollback dumps, manual ones, reset-content dumps and the panel restore's safety dump all counted in the same 14. A day
with several deploys pushed out nightly backups, so the daily recovery window shrank without anyone deciding it.
Part of Daneshbonyan re-evaluation track 7 (evaluator reason 4: database maturity).

Design doc

docs/features/db-maturity/RESEARCH.md section 5.3 (retention bullet) and ADR-028 in docs/engineering/DECISIONS.md.
Decision made by the lead agent and stated in the task contract (the owner may change it in review): two pools.

What changed:

  • prune(keep, keep_other=None): scheduled backups (reason scheduled, empty, missing, or an unreadable manifest)
    keep configured_keep(); every other reason keeps BACKUP_KEEP_OTHER (new setting, default 5, clamped 1..100,
    documented in .env.example).
  • Safety rule: prune never leaves zero verified scheduled backups. If the newest scheduled backups failed verify, the
    newest verified one is kept even beyond the keep number.
  • The scheduler passes both numbers. The Backups page states both numbers in plain Persian; Settings > Backup now says
    its number limits the nightly backups and that other backups are counted apart.
  • On SQLite (test backend) the old single pool is unchanged, and the pages say so honestly.

Tests

  • Oracle written by the reviewing agent from the task contract before it saw the code
    (test_backup_retention_oracle.py, not committed): 13 failed / 3 passed on the base; 17/17 on this branch (its one
    page case was corrected to render the PostgreSQL engine answer, plus a SQLite control).
  • Committed: tests/test_backup_retention_pools.py (36 + 2 cases): a deploy dump never evicts a scheduled one; the 6th
    "other" dump evicts the oldest "other" one; manifests without a reason count as scheduled; the safety rule; both page
    texts. Red first (33 failed), then green; 3 deliberate mutations each caught.
  • Lead agent's last run on this commit: oracle + pool tests + legacy-engine test, 56 passed.

Security

No security impact: no new route or door; the list response gains one number for the existing admin-only page.

AI assistance

Written by Claude agents in a Herdr team: implementer drill-t1-impl (Opus 5.5) wrote the code, tests and docs;
reviewer drill-t1-rev (Opus 5.5) wrote the oracle first and reviewed (accepted, 1 low label fix applied); lead
foreman7 (Opus 5.5) re-ran the oracle on the base and the patch. Log entry in docs/engineering/AI_ASSISTANCE_LOG.md.

Human review

sinashamsizadeh and others added 4 commits October 8, 2026 09:58
prune() kept the newest 14 backups of any reason. Pre-deploy, rollback,
manual, reset-content and pre-restore safety dumps used the same 14
slots, so a day with several deploys pushed the nightly backups out and
the daily recovery window shrank without anyone deciding it.

Now two pools: nightly backups (reason "scheduled", or no reason at all
for old manifests) keep the admin setting; every other reason keeps the
newest BACKUP_KEEP_OTHER (new setting, default 5, 1 to 100). Prune never
leaves zero verified nightly backups: if the newest ones all failed
verify, the newest verified one is kept beyond the limit. The Backups
page states both numbers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Settings > Backup still called the number "حداکثر نسخه‌ها" (maximum
copies), but since the two retention pools it limits only the nightly
backups. On PostgreSQL installs the label now says nightly automatic
backups, and the help text says the other backups (manual, before an
update, and so on) are counted apart with their own number. SQLite
installs, which have one pool, keep the old wording.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in main up to a38acb6 (#180, off-site SFTP copies and their
admin panel). Conflicts, all additive, both sides kept:
- .env.example and app/config.py: #180's OFFSITE_SFTP_* / OFFSITE_GPG_* /
  OFFSITE_REMOTE_KEEP settings first (they continue the off-site block),
  then BACKUP_KEEP_OTHER.
- static/admin/js/infra_backups.js: load() now calls both
  renderSchedule(data.schedule, data.keep_other) and
  renderOffsite(data.offsite).
The router merged on its own: the list response carries both keep_other
and offsite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@sinashamsizadeh
sinashamsizadeh marked this pull request as ready for review October 10, 2026 09:52
@sinashamsizadeh
sinashamsizadeh merged commit 90b72c2 into main Oct 10, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant