Retries and timeouts
Not climbed yetModel answer
Retry failures that are probably temporary: a timeout, a dropped connection,
503 Service Unavailable,429 Too Many Requests(after theRetry-Aftertime).Do not retry failures that will fail again:
400,401,403,404, a validation error. And never blindly retry a request that is not idempotent (charging a card, sending an SMS), because the first attempt may have succeeded even though you got no response. Make it idempotent first, with an idempotency key.Cap the attempts (2 or 3) and the total time spent.
Model answer
Retries multiply load exactly when the service has the least to spare: three retries mean up to four times the traffic. If each layer in a chain of three retries three times, one user request can become 64 calls at the bottom. The service never gets room to recover.
- Exponential backoff with jitter: wait 100 ms, 200 ms, 400 ms, each randomised, so clients spread out instead of retrying in waves.
- A retry budget: allow retries only up to, say, 10% of normal traffic.
- Retry at one layer only, usually closest to the user, not at every hop.
- A circuit breaker: after repeated failures, stop calling and fail fast for a while.
Model answer
Each caller's timeout must be shorter than its own caller's. If A gives up after 2 seconds but B waits 5 seconds for C, B keeps working on a request nobody is waiting for, and holds threads and connections doing it.
Pass a deadline down the chain rather than fixed numbers: A says "answer by 10:00:02.000", B subtracts the time it has used and gives C what is left (gRPC does this with deadlines). When time is up, every layer stops.
Set the base value from measured latency: a little above the dependency's p99, not the library default (often unlimited or 30 seconds).
Model answer
Not in delivery. Over an unreliable network a sender cannot tell "it failed" from "it worked but the reply was lost", so it must either retry (and risk running twice) or not (and risk running zero times). This is the two generals problem.
What you can build is effectively once: deliver at least once (keep retrying) and make the receiver idempotent, so duplicates have no extra effect. An idempotency key, a unique constraint on a message id, or a naturally idempotent operation ("set status to paid") does that.
When a system advertises exactly-once (Kafka transactions, for example), it means exactly-once processing within that system, built this way internally, not across your external side effects.
