Skip to content

fix(idempotency): expire cache in-progress records at their deadline - #5863

Open
svozza wants to merge 2 commits into
mainfrom
fix/idempotency-cache-deadline-ttl
Open

svozza wants to merge 2 commits into
mainfrom
fix/idempotency-cache-deadline-ttl

Conversation

@svozza

@svozza svozza commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Summary

CachePersistenceLayer recovered orphaned in-progress records by reading the record, taking a lock key, and then overwriting it unconditionally. The overwrite trusted a read taken before the lock, so a record that changed in between was destroyed: a completed result could be overwritten after a stale read, and a record freshly acquired by another invocation could be replaced while that invocation was running, leading to concurrent execution. In-progress records now expire at their execution deadline, so an invocation that timed out leaves nothing behind to recover and the next caller acquires the key with a single conditional write. The lock and the overwrite are removed.

Changes

  • Write in-progress records with a TTL ending at the execution deadline, rounded up to whole seconds and capped at the overall expiry; records without a deadline keep the overall expiry
  • Round overall-expiry TTLs down, so a record never stays in the cache past its expiry
  • Remove the orphan lock, the orphan classification, and the unconditional overwrite; on a conflict, return the existing record to the handler, retrying the write once if the record expired between the write and the read
  • Keep the SET option shapes for both @redis/client and @valkey/valkey-glide from fix(idempotency): pass cache SET options in the Valkey Glide shape #5737 unchanged
  • Replace the documentation section that described the lock-based recovery
  • Add fast-check property tests that run multiple callers through the adapter over random interleavings, and that check the rounding of both TTLs
  • Add a regression test for the stale read and update the unit tests for the removed recovery path

Upgrade note: in-progress records written by a previous version keep their full-expiry TTL. If the invocation that wrote one timed out, retries with the same payload are rejected as in progress until the record expires (expiresAfterSeconds), rather than being recovered after the deadline. This only affects invocations that timed out shortly before the upgrade, and stops once those records expire.

Issue number: closes #5714


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/XL PRs between 500-999 LOC, often PRs that grown with feedback label Oct 29, 2026
@svozza
svozza requested a review from dreamorosi October 29, 2026 22:32
@svozza
svozza requested a review from dreamorosi November 1, 2026 13:06

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL PRs between 500-999 LOC, often PRs that grown with feedback

2 participants