What is Retry pattern and how to use it correctly
Structured Java interview answer with junior, middle, and senior-level explanation.
🟢 Junior Level
Retry is an automatic re-call of a service on a transient error.
Call 1 → error (timeout)
Retry 1 → error (timeout)
Retry 2 → success! ✅
Why: Transient errors happen often (network, timeout, overload). Retry gives the service time to recover.
🟡 Middle Level
Resilience4j Retry
RetryConfig config = RetryConfig.custom()
.maxAttempts(3) // 3 attempts
.waitDuration(Duration.ofSeconds(1)) // wait 1 sec between attempts
.retryExceptions(TimeoutException.class, ConnectException.class)
.build();
Retry retry = Retry.of("backend", config);
Supplier<String> decorated = Retry.decorateSupplier(retry,
() -> backendService.call());
Exponential backoff
RetryConfig config = RetryConfig.custom()
.maxAttempts(5)
.waitDuration(Duration.ofMillis(500))
.intervalFunction(IntervalFunction.ofExponentialBackoff(500, 2.0))
.build();
// 500 = initial delay (ms), 2.0 = multiplier (exponential).
// 500ms → 1000ms → 2000ms → 4000ms ...
// Attempts: 500ms → 1s → 2s → 4s → 8s
Common mistakes
- Retrying all errors:
404 Not Found → retry is useless 500 Internal Error → retry may help Solution: retry only retryable errors
🔴 Senior Level
Retry with jitter
// Jitter prevents thundering herd
// Thundering herd — when many clients retry simultaneously,
// creating a new load spike. Jitter "smears" retries over time.
Random random = new Random();
long waitTime = baseDelay + random.nextInt((int)(baseDelay * 0.1)); // jitter 10% of delay
Production Experience
Spring Retry:
@Retryable(
value = {RetryableException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
// @Recover — method called when all retries are exhausted.
// Signature: same return type + Exception as first parameter.
public String callService(String input) {
return restTemplate.getForObject(url, String.class);
}
@Recover
public String recover(RetryableException e) {
return "fallback";
}
Best Practices
✅ Exponential backoff
✅ Jitter to prevent thundering herd
✅ Retry only for retryable errors
✅ Attempt limit
✅ Fallback after all retries exhausted
❌ Retry for 4xx errors
❌ Without backoff
❌ Without attempt limit
❌ Retry for idempotent operations
🎯 Interview Cheat Sheet
Must know:
- Retry — automatic re-call on transient error (timeout, network)
- Exponential backoff: 500ms → 1s → 2s → 4s — gives the service time to recover
- Jitter adds randomness to prevent thundering herd
- Retry only for retryable errors (TimeoutException, ConnectException)
- 4xx (Not Found, Bad Request) — retry is useless, server won’t fix itself
- maxAttempts is mandatory — infinite retry = hang
- Fallback after exhausting all attempts
Frequent follow-up questions:
- What is thundering herd? Many clients retry simultaneously — jitter “smears” retries over time.
- Which errors are retryable? Timeout, connection refused, 5xx. NOT retryable: 4xx, business exceptions.
- Exponential backoff formula? delay = baseDelay * multiplier^attempt, e.g. 500ms, 1s, 2s, 4s.
- Spring Retry vs Resilience4j? Spring Retry is simpler, Resilience4j is more flexible (bulkhead, circuit breaker, time limiter).
Red flags (NOT to say):
- “Retry for all errors” — no, retrying 404 is useless
- “Retry without limit = more reliability” — no, infinite retry = system hang
- “Jitter is not needed, servers will handle it” — without jitter, thundering herd occurs
- “Retry = idempotency doesn’t matter” — critical, duplicate request is possible
Related topics:
- [[20. What is exponential backoff]]
- [[17. How to ensure fault tolerance of microservices]]
- [[5. What is Circuit Breaker pattern]]
- [[4. What are compensating transactions]]
- [[15. How to organize communication between microservices]]