Code lab · World 02 · Failure-First Design
Idempotency-key store.
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.
Saving also stores the Python runtime (~13.5 MB, once for every lab) so this lab runs with no internet.
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:
- Compare request bodies with the given
fingerprint(body), so{"a": 1, "b": 2}and{"b": 2, "a": 1}are the same request. - A record is forgotten
ttl_secondsafter it was created (Day 2, chapter 4): after that the key is new again. Read time only throughclock.now(). - Store a copy of the response. A caller that mutates the dict it got back must not change what later retries receive (Redis stores serialized JSON for the same reason).
Python 3.14 loads on first run (~6.5 MB once)14
Loading the editor…
⌘/Ctrl + Enternot run yet · 8 s limit
press Run tests
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 Python restarts, your code stays.