Overview
The retry pattern re-executes an operation that failed due to a transient fault, such as a dropped connection, a timeout, a 503, or throttling. Many failures in distributed systems are momentary, so a well-behaved retry turns them into successes invisible to users.
Retries must be safe and controlled: retry only idempotent operations (or use idempotency keys), only retryable errors, with exponential backoff and jitter, a small maximum number of attempts, an overall deadline, and ideally a retry budget. Uncontrolled retries are a leading cause of cascading failures and retry storms.
If nobody answers the door, you wait a moment and knock again, maybe twice more with longer pauses. You do not pound on the door nonstop, and after a few tries you leave a note instead.
When to use it
- Network calls that occasionally fail transiently.
- Cloud APIs with throttling (429) responses.
- Database failovers and connection resets.
- Message processing with temporary downstream issues.
Where it shows up in interviews
Recognize it when: calls occasionally fail with timeouts or 503s.
- Design a client SDK for an API
- Design a payment processor integration
Where it is used in real software
Standard and adaptive modes with exponential backoff, jitter, and client-side rate limiting.
Service config defines retryable status codes, max attempts, and backoff.
Limit retries to about 10% of requests per client to avoid overload amplification.
Key terms
- Transient fault
- Temporary failure likely to succeed if retried.
- Backoff
- Waiting longer between successive attempts.
- Jitter
- Randomness added to delays.
- Retry budget
- Cap on retries as a fraction of requests.
- Deadline
- Total time allowed including all attempts.
How it works, step by step
- 1Fail the attempt quickly
Per-attempt timeout.
- 2Check retryability
Error type and operation idempotency.
- 3Back off with jitter
Random delay up to an exponentially growing cap.
- 4Stop at limits
Max attempts, deadline, budget, or open circuit breaker.
- 5Surface the failure
Return a clear error or fallback, log with context.
Which failures to retry
HTTP and gRPC responses
| Response | Retry? | Reason |
|---|---|---|
| Connection reset / timeout | Yes (if idempotent) | Transient network issue |
| 429 Too Many Requests | Yes, honor Retry-After | Throttling |
| 503 Service Unavailable | Yes, with backoff | Temporary overload |
| 400 / 422 | No | Request is invalid |
| 401 / 403 | No (refresh token first) | Auth problem |
| 409 Conflict | Depends | Re-read state first |
NOWResponse: Connection reset / timeout | Retry?: Yes (if idempotent) | Reason: Transient network issue
Retrying non-retryable errors wastes resources and hides bugs.
Implementation
import java.time.Duration;import java.util.concurrent.Callable;import java.util.concurrent.ThreadLocalRandom; public final class Retry { public static <T> T withBackoff(Callable<T> op, int maxAttempts, Duration base, Duration cap) throws Exception { for (int attempt = 0; ; attempt++) { try { return op.call(); } catch (Exception e) { if (!isRetryable(e) || attempt + 1 >= maxAttempts) throw e; long ceiling = Math.min(cap.toMillis(), base.toMillis() * (1L << attempt)); long sleep = ThreadLocalRandom.current().nextLong(ceiling + 1); // full jitter Thread.sleep(sleep); } } } private static boolean isRetryable(Exception e) { return e instanceof java.net.SocketTimeoutException || e instanceof java.net.ConnectException || (e instanceof HttpStatusException h && (h.status() == 429 || h.status() >= 500)); } public static final class HttpStatusException extends Exception { private final int status; public HttpStatusException(int status) { super("HTTP " + status); this.status = status; } public int status() { return status; } }}Complexity and performance
Per layer that retries.
Bound by a deadline.
Trade-offs
Retries improve success during blips but increase load exactly when a dependency is struggling.
Users wait longer for requests that need retries; keep attempts few and fast.
Variants and related techniques
Send a parallel duplicate after a delay rather than waiting for failure.
Delay queues and retry topics for background work.
Common mistakes
- Retrying at every layer.
Fix: Retry at a single layer to avoid multiplicative storms.
- Retrying POSTs without idempotency keys.
Fix: Duplicate orders or charges; add idempotency first.
- Fixed delays without jitter.
Fix: Synchronized clients hit the service in waves.
Interview questions
What makes a retry strategy safe?
Retrying only idempotent operations and transient errors, exponential backoff with jitter, a low attempt limit, an overall deadline, respecting Retry-After, retry budgets, and circuit breakers to stop when a dependency is down.
What is a retry storm?
When many clients or layers retry a failing service simultaneously, multiplying load and preventing recovery. It is prevented with backoff, jitter, budgets, single-layer retries, and circuit breakers.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Implement retry with jitter and a deadline | Easy | Backoff. |
| Design retry policy across 3 service layers | Medium | Amplification. |