Deadlines, retries, and idempotency
Compose bounded attempts with propagated deadlines and a durable idempotency boundary that preserves operation semantics.
On this page
The question for today
What you’ll understand
- Allocate an end-to-end deadline across attempts, backoff, and response work.
- Calculate retry amplification across independently retrying layers.
- Define an idempotency record's scope, atomicity, conflict behavior, and retention.
A timeout says the caller stopped waiting; it does not say the server did nothing. The server may have committed a payment or job immediately before the connection failed. Retrying can repair a lost response, amplify overload, or duplicate a side effect depending on the API contract. This lesson combines time accounting with durable operation identity so that retry policy becomes a deliberate part of correctness rather than a generic exception handler.
Carry one useful deadline through the call tree
An end-to-end deadline limits how long the caller can usefully wait. Each downstream attempt must fit within the remaining budget, including queueing, connection establishment, computation, and time to return a result. Propagating a relative remaining duration avoids assuming perfectly synchronized clocks across hosts. Cancellation should release queued and active work, but applications must cooperate; receiving a cancellation signal does not automatically reverse an already committed side effect. Include cleanup and response serialization in the budget instead of giving every child the full original timeout.
Choose attempt timeouts using measured latency distributions and an acceptable false-timeout rate for the relevant request class. Then test cold connections, deployment events, and dependency slowdowns. A timeout that covers only the socket read may omit DNS or TLS work in some client implementations. When the remaining budget is too short for a useful attempt, stop. Record deadline-exceeded outcomes separately from explicit permanent rejection, and retain enough trace context to determine whether the remote operation later completed.
Give retries one owner and a finite resource budget
Classify errors by whether another attempt is semantically safe and plausibly useful. Invalid input is usually permanent; a transient transport failure may be retryable only if operation identity is preserved. Let one appropriate layer own retries, cap attempts and total retry rate, and add randomized delay when many callers could synchronize. Jitter spreads attempts in time but creates no capacity. Backoff without a global or per-client retry budget can still sustain overload, particularly when a large population retries at the same capped interval.
If each of several nested layers retries independently, a single user operation can trigger multiplicative downstream attempts. An outer layer often cannot tell that the inner layer already spent its budget. Propagate attempt metadata and avoid retrying explicit overload responses when the contract says to stop. Monitor started attempts per original operation, remote resource cost, and successful useful completions. A rising success rate among individual attempts can hide that the service is spending most of its capacity repeating work that the user has already abandoned.
Place idempotency at the side-effect boundary
An idempotency key identifies a logical operation, not merely a transport attempt. Scope it to the authenticated principal and operation type, store a fingerprint of relevant input, and reject reuse with incompatible input. The deduplication record and the protected side effect must be committed atomically where possible. Otherwise a crash between the side effect and record creation permits duplication. When the effect crosses an external service boundary, use that service's idempotency facility or an explicit state machine and reconciliation process; a local cache entry cannot make a remote effect atomic.
Specify behavior for concurrent duplicates, in-progress operations, stored responses, failed attempts, and key expiration. Retention must cover the promised retry horizon; after expiration the same key may become a new operation according to the contract. The reliability lab only counts replicas and majority availability. Use it to ask where the durable idempotency record lives during failures, while remembering that a majority being up does not implement a transaction, consensus, or exactly-once external effects. Correctness comes from the protocol surrounding the record.
Work it through
A bounded retry and its dangerous alternative
An operation starts its attempt sequence with 900 ms of deadline budget. Reserve 100 ms for returning the result. The first attempt uses 250 ms, backoff is 100 ms, and the proposed second attempt limit is 400 ms. Separately compare three layers allowing three total attempts each.
Time spent before the second attempt is 250 + 100 = 350 ms. After reserving return time, 900 − 350 − 100 = 450 ms remains available to it.
A 400 ms second-attempt limit fits: total worst-case accounting is 250 + 100 + 400 + 100 = 850 ms, leaving 50 ms margin.
Three independently retrying layers with three total attempts each can create 3³ = 27 leaf attempts for one original operation, before any hedging.
The time budget permits the second attempt but does not establish semantic safety. A durable operation identity and an eligible error are still required; retries should have one owner.
The living notebook
Explore the model
Interactive lab · reliability
Where does operation identity survive?
Change replica count and unavailable count to see up = n − k against majority = floor(n/2) + 1. Consider the availability of a replicated idempotency store without claiming the diagram implements its protocol.
- Available replicas
- 4 of 5
- Majority needed
- 3
- Majority available?
- Yes
A replica count and a majority threshold, without a consensus protocol. Playback highlights fixed replica states. Having a majority alone does not establish linearizability, durability or failure independence. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forA reachable majority is not proof of atomic deduplication or exactly-once side effects.
Pause & reason
Check your understanding
Your turn
Try it yourself
An operation starts its attempt sequence with 700 ms of deadline budget and reserves 80 ms for returning its result. Its first attempt consumes 200 ms and backoff consumes 70 ms. What is the largest next-attempt budget under this accounting? Explain why reusing a key with different payment input should be rejected.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Subtract elapsed work and the response reserve from the remaining original budget; treat the key as a logical operation identity.
Compare with the solution
The maximum accounted budget is 700 − 200 − 70 − 80 = 350 ms, before any extra safety margin. Different payment input under the same scoped key is ambiguous: silently replaying or applying it can violate the caller's intended operation. Store a request fingerprint and reject a conflicting reuse according to the documented API contract.
Keep the idea
Take it with you
- A timeout does not reveal whether a remote side effect committed.
- Propagate one deadline and bound total retry amplification.
- Durable idempotency requires explicit scope, atomicity, concurrency, and retention semantics.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.