A retried payment returns the first response instead of charging twice, a reused key with a different body is refused, and failures can be retried.
The client sends Idempotency-Key: pay_abc123 with a payment. The network drops the response, the client retries with the same key, and the server must return the first response instead of charging again (Day 2, sections 2.1 to 2.4). Build the in-memory version of the lesson's IdempotencyStore, with the same record states: processing, completed, failed.
Implement IdempotencyStore(clock, ttl_seconds=86400):
| Method | Does |
|---|---|
check_and_set(key, request_body) |
(True, None) for a new key, which it claims as processing. (False, response) for a completed key. Raises IdempotencyInProgressError while the key is processing, and IdempotencyConflictError when the key was used with a different body. A failed key is claimed again: (True, None) |
complete(key, response) |
marks the key completed and stores the response. Raises ValueError for an unknown key |
fail(key, error) |
marks the key failed so a retry can run again |
handle(key, request_body, process) |
the lesson's with_idempotency: on a new key call process() once, store and return its response; on a completed key return the stored response without calling process; if process raises, mark the key failed and re-raise |
Rules the tests check:
fingerprint(body), so {"a": 1, "b": 2} and {"b": 2, "a": 1} are the same request.ttl_seconds after it was created (Day 2, chapter 4): after that the key is new again. Read time only through clock.now().In JavaScript and TypeScript the class is IdempotencyStore(clock, ttlSeconds = 86400) with checkAndSet(key, requestBody), complete, fail and handle. The pairs are arrays ([true, null], [false, response]), records is a Map of { requestHash, status, response, createdAt }, and complete on an unknown key throws a RangeError.
Write your code, then run 3 example tests and 10 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.