[The Mutex at the Door: Durable Object Idempotency]
Durable Object mutex for at-most-once gateway acceptance—not formal exactly-once across queues and exchanges.
Sources: papers/sections/04-mechanisms.tex, threat-model appendix notes, docs/enduser/concepts/idempotency.mdx.
Thesis
Webhook platforms retry. Networks drop acks. In trading, “please try again” is how double positions are born. HOOX’s answer is not a hopeful KV flag: it is a Durable Object mutex—single-threaded execution, SQLite-backed storage, atomic check-and-store under blockConcurrencyWhile.
Claim (precise): at-most-once acceptance at the gateway within TTL.
Non-claim: formal exactly-once across Queues (at-least-once) and exchange APIs.
1. The double-fill story
Without dedup: fill succeeds, response lost, client retries, second fill.
With the DO: second attempt collides on the composite key inside the TTL and is rejected (e.g. 409).
2. Algorithm (paper Algorithm 1)
Input: key k, TTL τ
Output: true if new; false if duplicate
enter blockConcurrencyWhile
e ← storage.get(k)
if e ≠ null and (now − e.storedAt) < τ then
return false
storage.put(k, { storedAt: now })
schedule alarm at now + τ
return true
Default TTL 300 s. Alarms reclaim storage so the object is not an infinite ledger of every trade ever attempted.
3. Key granularity — deliberate trade-off
Key shape:
trade:{exchange}:{symbol}:{action}:{quantity}
Excludes free-form requestId and timestamp. Two legitimate identical tickets inside the window are treated as one—retry safety over rapid scale-in. Design around the TTL if you need burst identical size-ins; do not design around accidental double webhooks.
4. Fail-open vs fail-closed (the load-bearing asymmetry)
| Concern | Posture | Rationale |
|---|---|---|
| Authentication | Fail closed | Forged trade is catastrophic |
| Idempotency DO unavailable | Fail open | Possible duplicate recoverable; frozen market not |
Documented in the threat model; tested; not an accident. Production rule from the paper’s engineering patterns:
Authentication fails closed; deduplication fails open.
5. Why DO, not KV
KV is brilliant for config and rate windows—eventual consistency. Wrong tool for “exactly one of two concurrent POSTs may proceed.” Durable Objects give strong consistency for one key space, no get/put interleaving under concurrency, and alarms without a janitor worker.
Planned evolution: exchange-native client order ids (newClientOrderId / orderLinkId) derived from requestId—venue-side cooperation beyond gateway TTL.
Sister object: ExchangeConnectionManager amortizes WebSockets; it is not the idempotency path (essay 11).
Previous: Seven-stage signal · Next: Service Bindings fabric