Idempotency Keys Lab (Interactive)
Charge $500 over a connection that drops after commit, then retry, race, and tamper the same key to probe the state machine. Drive a POST /v1/charges through the STARTED/COMPLETED key table: safe retries replay cached responses, in-flight collisions get 409, and mutated payloads get 400 — or watch the ledger double-charge without keys.
Idempotency-Key Retry Safety Lab
Charge $500 on a flaky connection, retry, and see whether the ledger or the cached response wins.
Actually charged
$0.00
idempotency rows
0
idempotency_records table
empty — first POST will atomically INSERT (key, hash, STARTED)
Request log
No requests yet.
How It Works Under the Hood
Networks cannot tell the client whether a committed-but-unacknowledged POST succeeded, and the two generals problem means retries are mandatory for payments. The fix is a client-generated UUID key plus a server table storing (key, request_hash, status, cached response). First execution atomically inserts STARTED, runs domain logic, and freezes the response as COMPLETED; replays skip logic and return the cached body byte-for-byte; concurrent same-key requests collide at 409; reusing a key with a different payload hash is rejected at 400 to stop key hijacking.
Core Architectural Principles
- Key + SHA-256 request fingerprint decides execute, replay cached 200, 409 in-flight, or 400 mismatch.
- Cached response body and status code replay identically so clients can retry aggressively forever.
- Rows carry a 24h-28d TTL; without keys every dropped response risks a real double charge.
For any payment or reservation design, specify client-generated idempotency keys stored with a payload hash and cached response, and walk the three cases explicitly: new key executes, COMPLETED replays cache, STARTED returns 409. Naming the hash-mismatch 400 defense shows you have thought about key-reuse attacks, not just happy paths.
Bulletproof retries cost a locking table, key storage lifecycle, and disciplined client key generation.