You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(queue): make claimPrActuationLock atomic, add a real concurrency test
Review findings on this PR:
- claimPrActuationLock was a non-atomic getTransientKey-then-putTransientKey
pair, so two genuinely concurrent deliveries for the same PR could both
observe an absent key and both proceed — defeating the exact race this
mutex exists to close. The sibling claimAgentMaintenanceLock (#2129,
#2368) already solved this: env.SELFHOST_TRANSIENT_CACHE.claim performs
the check-and-set as one atomic operation (Redis SET NX server-side),
with a documented fallback to the old get/set pair for a cache adapter
that hasn't implemented claim yet. Mirrored that exact pattern here.
- The existing lock tests only pre-seeded the key before the call started,
proving the contended branch but not the actual race. Added a Promise.all
test that fires two draft-dodge deliveries for the SAME PR with neither
pre-claiming anything, asserting exactly one PATCH and one completed
audit row. Verified this test is meaningful by temporarily reverting to
the non-atomic implementation and confirming it fails (2 PATCH calls),
then restoring the fix and confirming it passes.
- Exported claimPrActuationLock/releasePrActuationLock (matching the
already-exported claimAgentMaintenanceLock/releaseAgentMaintenanceLock)
and mirrored that sibling's full direct-unit-test suite — fail-open on a
broken cache, fail-open when claim() itself throws, atomic-claim-used
verification, and the no-claim-method fallback — closing the branch
coverage gap the new code left in the fallback/catch paths.
it("REGRESSION (#2135): claimPrActuationLock uses an atomic check-and-set, so two genuinely concurrent claims for the SAME PR can never both succeed", async () => {
expect(patchCalls).toHaveLength(1); // exactly one delivery won the race and closed the PR
11990
+
const audit = await env.DB.prepare("select count(*) as n from audit_events where event_type = ? and outcome = 'completed'").bind("github_app.draft_dodge_closed").first<{ n: number }>();
11991
+
expect(audit?.n).toBe(1); // exactly one completed close recorded — not two (the race), not zero
11992
+
});
11993
+
11895
11994
it("no-ops when no prior gate failure exists for the PR", async () => {
0 commit comments