Question 19 · Section 17

What is Retry pattern and how to use it correctly

Structured Java interview answer with junior, middle, and senior-level explanation.

Language versions: English Russian Ukrainian

🟢 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

  1. 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]]