Skip to content

fix: add processing lease to idempotency store - #453

Open
nasalehj wants to merge 1 commit into
ChainForgee:mainfrom
nasalehj:fix/idempotency-processing-lease
Open

fix: add processing lease to idempotency store#453
nasalehj wants to merge 1 commit into
ChainForgee:mainfrom
nasalehj:fix/idempotency-processing-lease

Conversation

@nasalehj

Copy link
Copy Markdown

Summary

Fixes #429. The idempotency store treated a processing record as permanent until complete() ran — and complete() was fire-and-forget:

  • A crash orphaned the key. If the process died between acquire and complete, the row stayed processing forever and every retry with the same Idempotency-Key returned 409 indefinitely.
  • Replay-after-success raced to 409. Because complete() wasn't awaited, a fast retry could observe processing after the first response had already been delivered, violating the "same key ⇒ same result" guarantee.

Changes

  • Lease on in-flight records. tryAcquire stamps lease_expires_at on processing records, and the middleware refreshes it via a heartbeat() interval while the handler runs, so a slow request is never mistaken for an abandoned one.
  • Atomic re-acquire of abandoned leases. A processing record whose lease has expired is atomically claimed (row-level lock) by the next request with the same key — a crashed request's key is recovered within a bounded window, and an abandoned lease can never run the handler twice concurrently.
  • Persist before deliver. The response is written only after complete() has committed, so a retry arriving after the first response replays the cached result (200) instead of returning 409.
  • Tests covering all three acceptance criteria: lease-expiry re-acquire, replay-after-success, concurrent acquire contention — plus heartbeat lease refresh and a live-vs-expired lease 409 distinction.
  • Coverage baseline updated for the new store/middleware code paths (the idempotency integration spec is excluded from the coverage run, so the baseline tracks the additional uncovered lines).

Closes #429

Processing records previously held a key forever: if the process died
between acquire and complete, every retry returned 409 until a manual
cleanup. Add a lease_expires_at column refreshed by a middleware
heartbeat while the handler runs, atomically re-acquire abandoned
leases, and persist the result before delivering the response so a
replay never observes processing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IdempotencyStore has no processing lease: a crashed claim submission is stuck returning 409

1 participant