Repository navigation
fix(attachments): pack after manual syncs in the background - #3
Merged
Merged
Conversation
A sync or import sent to the daemon packed attachments inside its own request, with no runtime limit. That pass also listed and re-verified every packed blob, so on a large archive each manual sync waited minutes after its mail sync to pack a few new attachments. - After a successful ingest, the request marks packing pending and asks the attachment-pack job to run through the new Scheduler.RequestJob, then returns. The job takes the operation gate after the request releases it, stops after one minute, and yields to waiting requests. The idle tracker counts it from the moment it queues, so a daemon started on demand stays up until the pass finishes. It cannot rely on the job's six-hourly tick, which it may never live to see. - An attachment-pack pass verifies one bounded window of packed blobs and queues follow-ups only while loose blobs remain. The daily attachment-maintenance job still finishes whole verification cycles. - The unpacked-blob queries take candidate hashes from the hash indexes and read attachment rows only for unpacked hashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01793XPgu1pMcfXjFd423RNP
…ures The import path no longer packs inside the request, so there is no pack warning to stream. Assert that a maintenance fault neither fails nor writes to an import request and that the pack request stays pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01793XPgu1pMcfXjFd423RNP
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01793XPgu1pMcfXjFd423RNP
A background pack pass has no request to report to, so its failures were visible only in the daemon log. The next sync, import, or message repair sent to the daemon now prints one line on stderr when the attachment-pack job's last pass failed, naming the error and `msgvault pack-attachments`. A clean pass clears the notice, and a notice that cannot be delivered never changes the ingest result. Tests now also cover the daily job chaining a whole verification cycle, the idle tracker counting the pack job while it waits for the gate, and the unpacked-blob queries against a row-by-row definition on seeded random archives. The docs no longer claim a pass's cost is independent of archive size: each pass still lists every hash and checks every pack file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01793XPgu1pMcfXjFd423RNP
arcaputo3
marked this pull request as ready for review
October 14, 2026 16:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A
syncor import sent to the daemon packed attachments inside its own request, with no runtime limit. That pass also listed and re-verified every packed blob in the archive. On an archive with about 29,000 packed blobs (31 GB), each manual sync therefore waited a minute or more after its mail sync to pack a handful of new attachments. On a daemon started on demand at low I/O priority, this took over two minutes on every run.The obvious alternative, leaving the work to the six-hourly
attachment-packjob, does not work for daemons started on demand. They stop after 20 idle minutes, so they may never be running when that job is due.What changes
attachment-packjob to run, then returns. The newScheduler.RequestJobbehaves like a cron tick, but is triggered on demand. It returns immediately, a run that is already waiting absorbs the request, and a run in progress gets one follow-up.attachment-packpass now verifies one window of at most 128 packed blobs or 32 MiB, using the existing durable cursor. It queues follow-ups only while loose blobs remain. Before, every trigger chained a whole verification cycle (about 1,100 passes on the archive above). The dailyattachment-maintenancejob still completes the cycle. Part of each pass still grows with the archive: it lists every attachment hash, checks every pack file, and reads the 256 loose-blob shard directories. On the archive above that is about 0.5–2 s per pass.msgvault pack-attachments. A clean pass clears it.Behavior notes
remove-account,gc,purge-excluded-media) still runs inside the request. It is unchanged here.Measurements
These were taken on a copy of a real archive: 63 GB SQLite, 154k attachment rows, 28.6k packed blobs (31 GB raw) in 841 packs, plus 7 new loose blobs (12 MB). They ran at foreground I/O priority.
attachment-packtriggerThe first row is the time between the sync's own work finishing and the request returning. On a daemon started on demand at background I/O priority, the same inline pass took 2 min 11 s. The daily
attachment-maintenancejob still chains the full verification cycle.🤖 Generated with Claude Code
https://claude.ai/code/session_01793XPgu1pMcfXjFd423RNP