fix(checkpoints): recover from stale git locks in the shadow store - #92127
Open
33hodl wants to merge 1 commit into
Open
fix(checkpoints): recover from stale git locks in the shadow store#9212733hodl wants to merge 1 commit into
33hodl wants to merge 1 commit into
Conversation
Killed git processes (gateway restart, crash, OOM) leave <target>.lock files behind in the checkpoint store. Every later command needing that target fails with rc=128 'Unable to create ...: File exists' until a human removes the lock by hand, silently disabling checkpoints for the affected project (observed in production: 18+ failures over 24h from locks abandoned 18 days earlier). Sweep locks older than _STALE_LOCK_MAX_AGE_S (600s; git holds a lock for seconds at most) from _init_store so the common path never fails, and retry once in _run_git on the exact lock-exists failure signature so every call site self-heals. Adds 3 regression tests.
Collaborator
Contributor
Tests cover all three layers: repair-only-removes-old, retry-succeeds-after-clear, and end-to-end snapshot-after-repair with mtime backdating. Minor notes:
|
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 git process killed mid-write (gateway restart with
KillMode=mixed, crash, OOM) leaves its<target>.lockfile behind in the checkpoint shadow store. Every later git command needing that target then fails with:(rc=128) until a human removes the file by hand. The checkpoint manager has no recovery for this — it silently returns
Falsefrom_take()and the affected project never gets snapshots again.Reproduction (production, Aug 2026)
Two abandoned locks sat in the store since 2026-08-04 and 2026-08-13 (killed git processes — no live process can hold a lock for that long). From that point on, every checkpoint snapshot of the affected project failed identically, 18+ times in a 24h window:
Manual repro of the same failure:
Git itself suggests "remove the file manually to continue" — exactly what the manager should do, safely, on its own.
Fix
_repair_stale_locks(store)— sweeps the store for.lockfiles older than_STALE_LOCK_MAX_AGE_S(600s) and unlinks them. Git holds a lock for the duration of a single command bounded by_GIT_TIMEOUT(30s), so anything older is abandoned by definition; a live concurrent git process is never disturbed (10-minute margin)._run_gitretry — on the exact failure signature (rc=128 +Unable to create+File exists), run the sweep and retry the command once. This covers every call site (snapshot, diff, restore, prune) in one place._init_storeproactive sweep — the shared entry point of every snapshot path clears stale locks before any git command runs, so the first operation never fails.Why a bounded retry instead of always sweeping before every git call: sweeping is only needed when a lock is actually present, and the retry makes the repair self-healing at the exact point of failure while the proactive sweep keeps the common path clean.
Validation
tests/tools/test_checkpoint_manager.py:test_repair_only_removes_old_locks— fresh lock kept, stale lock removed, count correcttest_run_git_retries_once_after_clearing_stale_lock— the exact production failure (stale lock on the per-project index) now succeeds via the retry pathtest_snapshot_succeeds_after_stale_lock_repair— end-to-end throughensure_checkpointTestSafeRestoreare pre-existing onmain— verified by stashing the change and re-running; unrelated safe-restore user-edit tests)ruff checkclean on both changed filesgit add -Afor the affected project succeeds