8000 Add an admin page showing the status of index backups by daoudclarke · Pull Request #538 · mwmbl/mwmbl · GitHub
[go: up one dir, main page]

Skip to content

Add an admin page showing the status of index backups - #538

Closed
daoudclarke wants to merge 5 commits into
mainfrom
claude/epic-johnson-ppm1oz-backup-admin
Closed

daoudclarke wants to merge 5 commits into
mainfrom
claude/epic-johnson-ppm1oz-backup-admin

Conversation

@daoudclarke
Copy link
Copy Markdown
Contributor

Stacked on #537. Merge that one first, then retarget this PR to main.

What changed

  • New IndexBackup model (migration 0050). backup_index writes one row per run:
    • The row starts as RUNNING, and the run updates bytes_read every GiB it reads.
    • On success it becomes SUCCEEDED, with the SHA-256. On an exception it becomes FAILED, with the error, and the exception is re-raised.
  • /admin/index-backups/, linked from the admin home page's Status table. It shows:
    • The last successful backup of the live index: when it finished, its key, its size and how long it took. It's flagged as overdue if nothing has succeeded in 35 days.
    • The 20 most recent runs, each with its status, progress and last update, plus its key or error. A RUNNING row with no progress for an hour is shown as stalled. That is also how a run shows up when the container was killed before it could record a failure.
  • The page reads only the database. It makes no B2 call and needs no backup credentials in the web process.

Why

A monthly cron job that fails quietly looks the same as one that works. This page shows when the last success was and what's running right now.

How it was verified

  • make check passes.
  • The new and updated tests pass, 14 in total:
    • test/test_admin_index_backups.py: staff-only access; overdue with no backups and with only an old success; a recent success; a running row versus a stalled one.
    • test/test_index_backup.py: a run is recorded as succeeded with the manifest's SHA-256; a failed run records its error; progress is reported once per interval, not once per chunk.

Generated by Claude Code

The index is the only copy of seed crawls, curation and Staan copies, so it
cannot be rebuilt from the crawl batches. backup_index streams the whole file,
zstd-compressed and SSE-C encrypted, to a bucket of its own, reading each 4 MB
chunk under a shared lock on its byte range so no page is caught half-written.
A manifest with the size and SHA-256 is written once the upload finishes.

restore_index downloads in ranges, checks the result against the manifest and
only then renames it into place. A Dokku cron entry runs the backup monthly;
docs/index-backup.md covers setup and restore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jZWrRVSzD1uuafxpb8fW2
Each backup_index run now records itself as an IndexBackup row: RUNNING with
its progress updated every gigabyte read, then SUCCEEDED with the SHA-256 or
FAILED with the error. /admin/index-backups/ reads only those rows, so it
needs no B2 call. It flags no success in 35 days as overdue, and a running
backup with no progress for an hour as stalled - which is also how a run the
container killed before it could record a failure shows up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jZWrRVSzD1uuafxpb8fW2
…mory

From review of the backup PR:
- botocore retries a request but not an error reading its body, so a dropped
  connection killed the whole restore. Each range is now retried with backoff,
  and a short body counts as a failure.
- A failed restore removes its .partial file instead of leaving up to 420 GB
  on the live volume and blocking the next restore; a killed one's is written
  over.
- Chunks are 64 pages, not 1024, so writers wait on a shared lock for one
  quick pread. The cron runs at best-effort priority 7 rather than idle, which
  could leave a lock held while search I/O kept the disk busy.
- s3transfer buffered ten 64 MB parts of the non-seekable stream; now five.
- Docs: retention by a lifecycle rule on the prefix, since each backup is a
  new file; cancelling unfinished uploads; checking SSE-C against real B2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jZWrRVSzD1uuafxpb8fW2
@codecov
codecov Bot commented Oct 21, 2026
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.30508% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mwmbl/indexer/backup.py 96.87% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@daoudclarke
daoudclarke added this pull request to stack #568 October 30, 2026 13:53
Base automatically changed from claude/epic-johnson-ppm1oz to main October 31, 2026 13:29
@daoudclarke
Copy link
Copy Markdown
Contributor Author

Superceded by #571

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.

2 participants

0