accesspatterns.dev
← All models

payments

Idempotency Keys

A conditional write that makes a retried request safe.

One table, one job: let the first attempt at a request through and reject every retry. A PutItem with attribute_not_exists on the key is the whole mechanism - the write either claims the key or fails, and the caller learns which without a read-then-write race.

The model

IdempotencyKey

A claimed request key and the outcome it produced.

pk
IDEMPOTENCY#<key>

Attributes: status (S), resultId (S), claimedAt (N), expiresAt (N)

Access patterns

  • PutItemClaim a request key

    Write the key only if nobody has claimed it, so a retry is refused rather than duplicated.

  • UpdateItemRecord the outcome

    Move a claimed key from in_progress to its final state.

  • GetItemRead a previous outcome

    Fetch what the first attempt produced, to return it again.

Design notes

The condition is the mechanismwhy

attribute_not_exists(PK) makes the write itself the mutual exclusion. Reading first and writing if absent looks equivalent and is not: two concurrent requests both read nothing and both write. The condition is evaluated where the item lives, so exactly one wins.

The claim is a design here, not a live demonstrationtrade-off

The preview console runs the read below, not the conditional write: the resolver does not yet emit a runnable guarded write, so there is no button here that races two claims and shows one losing. The engine itself does support the mechanism - a conditional put rejects a duplicate with 'The conditional request failed' - so this is a gap in what the page can demonstrate, not in what DynamoDB does.

A retry can arrive in three stateswhy

The seeded rows stage all three. A succeeded key returns its stored result. A failed key has to say whether it may be reclaimed, or a caller retries forever against a key that will never succeed. An in_progress key is the awkward one: the first attempt is still running, so there is no result to return and no safe way to start a second - the honest answer is a retryable conflict, not a fabricated success.

Claim before doing the work, not afterwhy

The key is written in_progress before the side effect runs, then updated with its outcome. Claiming afterwards would leave a window where a retry starts a second charge, which is the failure the whole pattern exists to prevent.

A failed condition is a normal outcometrade-off

The rejected retry is not an error to alert on: it is the pattern working. The caller reads the existing key and returns its stored result, so the client sees one answer no matter how many times it asks.

Taught in the course

idempotency-keys
Try an example

Fetch what the first attempt produced, to return it again.

PK(pk)
claimedAt
expiresAt
resultId
status
IDEMPOTENCY#pay-4f21a9
1786579200N
1787184000N
charge_8812S
succeededS
IDEMPOTENCY#pay-9c03be
1786622400N
1787227200N
charge_8813S
succeededS
IDEMPOTENCY#pay-1d77f5
1786663872N
1787268672N
in_progressS
IDEMPOTENCY#pay-6b2e40
1786492800N
1787097600N
failedS

Run an operation to see the raw engine response.

table idempotency-keyskeys PKitems 4

The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.