Idempotency & Retry-Safe Systems · Retries Done Right
Exponential Backoff and Jitter
Space out retries so recovering systems aren't flattened.
Immediate retries hammer a struggling dependency. Use exponential backoff (base × 2^attempt, capped) and jitter (randomization) so clients don't retry in synchronized waves. AWS's analysis shows full jitter (sleep = random(0, min(cap, base·2^n))) or decorrelated jitter spread load best. Cap attempts and total time, and honor server hints (Retry-After).
Backoff strategies
| Strategy | Load on recovering service | Completion time |
|---|---|---|
| Immediate retry | Worst (synchronized spikes) | Fast when it works |
| Fixed delay | Bad (synchronized waves) | Predictable |
| Exponential, no jitter | Better but clustered | Grows quickly |
| Exponential + full jitter | Best spread | Good |
| Decorrelated jitter | Very good | Good |
import random
def full_jitter(attempt: int, base: float = 0.1, cap: float = 10.0) -> float:
return random.uniform(0, min(cap, base * 2 ** attempt))
async def with_retries(op, attempts=4):
for n in range(attempts):
try:
return await op()
except Retryable as e:
if n == attempts - 1:
raise
await asyncio.sleep(e.retry_after or full_jitter(n))
const fullJitter = (attempt: number, base = 100, cap = 10_000) => Math.random() * Math.min(cap, base * 2 ** attempt);
export async function withRetries<T>(op: () => Promise<T>, attempts = 4): Promise<T> {
for (let n = 0; ; n++) {
try { return await op(); }
catch (err) {
if (!isRetryable(err) || n === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, retryAfterMs(err) ?? fullJitter(n)));
}
}
}
RetryConfig config = RetryConfig.custom()
.maxAttempts(4)
.intervalFunction(IntervalFunction.ofExponentialRandomBackoff(Duration.ofMillis(100), 2.0, 0.5, Duration.ofSeconds(10)))
.retryOnException(e -> e instanceof RetryableException)
.build();
Retry retry = Retry.of("inventory", config);
Supplier<Stock> call = Retry.decorateSupplier(retry, () -> inventory.stock(sku));
Common pitfalls
- No cap on the backoff or attempts.
- Jitter omitted in fleets of clients.
- Retrying non-retryable errors.
Key points
- Exponential backoff with full jitter
- Cap attempts and total time
- Honor Retry-After
Further reading: AWS: Exponential backoff and jitter
Practice questions
Why add jitter to exponential backoff?
Answer
Without randomness, clients that failed together retry together, creating synchronized load spikes; jitter spreads retries over time.
What is full jitter?
Answer
Choosing the sleep uniformly at random between 0 and the capped exponential backoff value for that attempt.
Open in the app to mark lessons complete, take notes and track your progress.