A fintech payments gateway processes millions of charge requests per day. To prevent double-charging when clients retry (due to timeouts or flaky mobile networks), the gateway uses an idempotency key: multiple concurrent requests with the same key must share the same result.
You are implementing an in-memory, thread-safe “idempotency ledger” for a single process.
Implement:
process_request_threadsafe(key: str, expensive_work: Callable[[], int]) -> int
Requirements:
process_request_threadsafe with the same key at the same time, exactly one thread may run expensive_work(). All other threads must wait and then return the same computed value.expensive_work() runs. (It’s fine to use a global lock for short state transitions.)expensive_work() raises an exception, the key must not be marked as completed. Waiting callers should be released, and a later call for the same key should be allowed to retry (and may run expensive_work() again).Example 1
key = "x", two concurrent callers, expensive_work() -> 77, and expensive_work executes once.Example 2
["a","b","c"] concurrently, each expensive_work returns 1,2,3[1,2,3], each key computed once; work for different keys can overlap.key = "x", two concurrent callers, expensive_work() -> 7Output7 (both callers), expensive_work executed onceWhyOne thread claims the key and computes; the other waits and then returns the published value.keys = ["a","b","c"] concurrently, expensive_work returns 1,2,3OutputResults [1,2,3], each key computed onceWhyEach key has independent state; work runs outside the lock so different keys can compute in parallel.1 <= total calls <= 2 * 10^51 <= unique keys <= 10^51 <= len(key) <= 641 <= threads <= 10^3expensive_work may be slow and may raise exceptions