Skip to content

fix(kanban): content-addressed corrupt-DB backup filename (refs #33529) - #33804

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-4f9b6288
May 28, 2026
Merged

fix(kanban): content-addressed corrupt-DB backup filename (refs #33529)#33804
teknium1 merged 1 commit into
mainfrom
hermes/hermes-4f9b6288

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Salvages the conceptual approach from PR #33529 (@hanzckernel) — content-addressed backup filename — at ~10% the LOC.

Summary

_backup_corrupt_db() used a timestamp + collision counter for the backup filename. Each retry on an unchanged corrupt kanban.db produced a new .corrupt.<timestamp>.bak copy of the same bytes. After 10 retry cycles (gateway dispatcher's 5-minute quarantine timer, multi-profile fleets sharing one DB, manual reopen attempts) you had 11x the disk footprint of duplicate corrupt data.

This PR makes the backup filename deterministic in a sha256 of the main DB. Same bytes → same filename → if not path.exists(): copy skips the work on retries. Different bytes (partial repair, further damage) → different fingerprint → second backup preserved.

Live verification

10 retries on the same corrupt DB (100KB):

Metric Before After
Backup files 11 1
Disk usage 1144 KB 104 KB
Amplification 11x 1x

Tests: 449/449 kanban tests pass (test_kanban_db.py + test_kanban_core_functionality.py + test_kanban_tools.py). New regression test test_repeated_corrupt_open_reuses_single_backup asserts the invariant directly + verifies mutated corrupt bytes still get a separate backup.

Why simpler than #33529

@hanzckernel's PR weighed in at +670/-70 across 5 files. This is +66/-21 across 2 files. Dropped without losing the bug fix:

  • Persistent .corrupt-quarantine.json marker file. Pure metadata. The deterministic backup file existing IS the marker — same property (idempotent retry), no separate state machine to maintain or clear.
  • Atomic temp + fsync + os.replace copy helper. shutil.copy2 is fine for a quarantine-only path. We're copying a known-broken DB; durability of the backup beats best-effort by no meaningful margin.
  • WAL+SHM sidecar fingerprinting at the backup layer. The main DB hash is what changes between distinct corruption incidents; sidecar variations don't meaningfully define "is this the same incident." Sidecar backups still happen (inherit the same content-addressed name from the main DB).
  • Gateway-side _pause_corrupt_board / _board_disabled_same_fingerprint helper extraction. DRY-only refactor in the original PR. Useful but orthogonal to the bug being fixed. Existing inline code paths in gateway/run.py already work.
  • Gateway-side WAL+SHM fingerprint extension. The existing (path, mtime, size) tuple still gives the 5-minute retry semantics it needs.
  • Health-probe and auto-decompose skip-corrupt-boards paths. Distinct concern (and a real latent issue) — not this bug. Defer.

The PR description for #33529 covers other issues (#30445, #31736) that the cluster sweep just closed via the WAL-init / connect_closing / quarantine-retry / torn-write defenses landed earlier this week. Those issues are resolved; the residual concern this PR addresses is purely the backup-file disk amplification on the retry path.

Credit

  • @hanzckernel — identified the bug, proposed the content-fingerprint approach, supplied the regression scenarios this PR's test is based on. Co-authored trailer on the commit.

Infographic

kanban-corrupt-backup-amplification

Repeated quarantines of an unchanged corrupt kanban.db used to amplify
disk usage by N: the gateway dispatcher's 5-minute retry loop, multi-
profile fleets sharing one DB, and manual reopen attempts each produced
a fresh '.corrupt.<timestamp>.bak' copy of the same bytes. After 10
retries on a 100KB DB you had 11x the disk footprint of duplicate
corrupt data.

Derive the backup filename from a sha256 of the main DB instead of a
timestamp + collision counter. Same bytes → same filename → skip the
copy on retries. Different bytes (partial repair, further damage) →
different filename → preserve separately. Sidecar (-wal/-shm) backups
inherit the same content-addressed name.

Inspired by @hanzckernel's PR #33529, simplified down to ~30 LOC: drop
the persistent JSON marker file, drop the atomic temp+fsync+rename
helper (shutil.copy2 is fine for a quarantine-only path), drop the
gateway-side WAL/SHM fingerprint extension (the existing
(path, mtime, size) tuple still gives the 5-minute retry semantics it
needs), and drop the gateway-side helper extraction. The backup file
existing IS the marker; no separate state needed.

Test: tests/hermes_cli/test_kanban_db.py::test_repeated_corrupt_open_reuses_single_backup
proves 10 retries on the same corrupt bytes produce 1 backup (was 11),
and mutating the corrupt bytes produces a second backup with a
different fingerprint.

Refs #33529
Co-authored-by: hanzckernel <zhicheng.han@mathematik.uni-goettingen.de>
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-4f9b6288 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9544 on HEAD, 9544 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 5029 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

Comment thread hermes_cli/kanban_db.py Dismissed
Comment thread hermes_cli/kanban_db.py Dismissed
Comment thread hermes_cli/kanban_db.py Dismissed
Comment thread hermes_cli/kanban_db.py Dismissed
Comment thread hermes_cli/kanban_db.py Dismissed
@teknium1
teknium1 merged commit 6f9182c into main May 28, 2026
24 of 25 checks passed
@teknium1
teknium1 deleted the hermes/hermes-4f9b6288 branch May 28, 2026 10:38
@alt-glitch alt-glitch added P3 Low — cosmetic, nice to have type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard labels May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants