compact: stop cleanly at a pack boundary on Ctrl-C, keep the saved index valid - #9890
Conversation
…dex valid Persist the index and exit with an error instead of finishing or leaving a stale index that forces a full rebuild.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9890 +/- ##
==========================================
- Coverage 85.24% 85.24% -0.01%
==========================================
Files 93 93
Lines 15570 15576 +6
Branches 2357 2360 +3
==========================================
+ Hits 13273 13277 +4
- Misses 1598 1599 +1
- Partials 699 700 +1 ☔ View full report in Codecov by Harness. |
Review by Claude OpusThe production change is correct and well-designed. I traced the consistency invariant carefully:
Style, comments, and imports are all in keeping with the surrounding code. 👍 Issue — the test does not exercise the fix (confirmed by running it)
Root cause: the test triggers I verified this empirically by replicating the test's exact trigger logic with the fix applied: Observed Consequences:
Suggested fix — gate the interrupt on actual pack deletions so it fires between packs: def store_delete_then_interrupt(name, **kwargs):
original_store_delete(name, **kwargs)
if name.startswith("packs/"): # only count/trigger on real pack deletes
calls.append(name)
if len(calls) == 1: # one Ctrl-C after the first pack is deleted
sig_int._sig_int_triggered = TrueWith this, the drop loop deletes exactly one pack (and removes that pack's index entries) before stopping — which is the scenario the PR is actually about. To make the assertions meaningful, also confirm the repo is genuinely in a partial state, e.g. Minor / non-blocking
VerdictShip the production change — it's correct and idiomatic. Please fix the test before merge: as written it green-lights without ever running the code it claims to cover, so it would not catch a regression in the pack-boundary stop logic. 🤖 Reviewed with Claude Code |
…t for keep/drop/forget
|
Fixed the test to trigger on the first actual pack delete instead of the archive-nuke call, and assert on the real pack count. Also switched keep/drop/forget to defaultdict(set) |
Summary
borg compactinvalidates the chunk index before deleting any packs, then rewrites it once at the end. If Ctrl-C hits mid-deletion, that rewrite never happens, so the next command finds no valid index and rebuilds it from scratch, scanning every pack. On a remote repository that can mean pulling the whole thing over the network.Now a single Ctrl-C stops the pack-deletion and pack-rewrite loops at the next pack boundary. The index for whatever was already processed gets saved before the command exits with an error, so nothing downstream needs a rebuild. A second Ctrl-C still aborts immediately, same as before.
Test plan
Closes #9830