Conversation
) Previously, an idempotency key was SET in Redis with a 24h TTL BEFORE runRedisTrack ran. If the deduction then failed (insufficient balance, transient Redis fault, etc.), the key was permanently 'poisoned' for 24 hours — every retry hit 409 duplicate_idempotency_key, even though no actual deduction had committed. Fix: add releaseIdempotencyKey() that deletes the Redis key, and wrap runRedisTrack / runRedisTrackV3 in try/catch in runTrackV2 + runTrackV3 — on any error, release the claim before re-throwing the original error. This preserves the original concurrent-request safety (two simultaneous retries still race on SET NX; only one proceeds) AND lets the caller safely retry after a failure. Unit tests added covering: atomic SET NX, release-then-retry, Redis-not-ready fail-open, swallowed del errors, and no-cross-talk between distinct keys (7/7 passing).
|
PR author is not in the allowed authors list. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
|
Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
Confidence Score: 5/5 - Safe to MergeSafe to merge — this PR correctly addresses an idempotency key leak by ensuring the key is released when a deduction operation fails, preventing resource exhaustion or duplicate-prevention bypass in retry scenarios. The fix is targeted and surgical, touching only the failure path in the track deduction logic without altering the success path or broader control flow. No issues were identified by automated review across all four changed files, and the change aligns well with the stated intent of the fix. Key Findings:
|
Fixes #1138.
The bug
/trackcalls with anidempotency_keypermanently 409 on retry after a single failure. The key isSET NXinto Redis with a 24-hour TTL beforerunRedisTrack/runRedisTrackV3runs, so if the deduction throws (insufficient balance, transient Redis fault, etc.) the key sits there blocking every retry for the next 24 hours — even though no deduction ever committed.The fix
Add
releaseIdempotencyKey()next tocheckIdempotencyKey()— same Redis key layout, justDEL. Then wrap therunRedisTrack/runRedisTrackV3call inrunTrackV2andrunTrackV3intry/catch. On any throw:This preserves the existing concurrent-request safety — two simultaneous retries still race on
SET NXand only one proceeds — while fixing the retry-after-failure path.I kept this strictly to the
body.idempotency_keypath. The internalgetTrackIdempotencyKey({ctx})Redis lock used insideexecuteRedisDeductionV2is unchanged; that's a per-deduction internal mechanism with its own lifecycle.Tests
New unit file
server/tests/unit/misc/idempotency/checkIdempotencyKey.test.ts— 7/7 green:Existing
handle-track-queue-fallback.test.ts(3/3) andtrackIdempotencyKey.test.ts(1/1) still pass.Notes for the author of #1138
Your proposed "move storage to after success" change works for the retry path you described, but it opens a window where two concurrent retries both pass the (not-yet-set) check and both perform the deduction. The rollback pattern here keeps concurrent-safety intact. I also left
body.skip_eventandhandleEventIdempotencyKey's commented-out event-insert block alone — those are a separate concern (sync-vs-queue event logging) and didn't seem worth bundling into a focused fix.Summary by cubic
Fixes a bug where
/trackrequests with anidempotency_keyreturned 409 on every retry after a failed deduction. We now release the key on errors so retries can proceed, while keeping concurrent safety intact.releaseIdempotencyKey()to deletetrack:<idempotency_key>in Redis when a deduction fails.runRedisTrack/runRedisTrackV3in try/catch insiderunTrackV2/runTrackV3; on error, release the key and re-throw.SET NXconcurrency guarantees; retries after failure no longer 409.Written for commit 5575252. Summary will update on new commits.