Reactive Resilience — Retry, Timeout, Circuit Breaker, Bulkhead
Fast recall
Quick revision
Core answer
WebClient calls layers four primitives: `timeout` bounds latency (always set per call), `retryWhen(Retry.backoff(...))` with jitter and an exception filter retries idempotent transient failures, circuit breaker (Resilience4j reactive) stops hammering a degraded upstream, and bulkhead isolates concurrency per downstream so one slow service can't drain capacity. Order matters in the operator chain — timeout innermost, then retry, then circuit breaker outermost — so retries respect timeout and the breaker counts retried failures correctly. Never retry non-idempotent writes without an idempotency key, and never retry 4xx.Whether the candidate composes resilience primitives correctly, understands operator order, idempotency, and Resilience4j reactive integration
Retrying non-idempotent writes; placing timeout outside retry; using fixed-delay retries; no jitter; no exception filter; no fallback
Discuss operator order rationale, jittered backoff, idempotency keys, hedged requests, bulkheads per downstream, and circuit breaker tuning by SLO
Interview-ready guide
Interview answer
Resilience for outbound WebClient calls layers four primitives — timeout, retry, circuit breaker, bulkhead — each solving a different failure mode.
The four primitives
- Timeout — bounds per-call latency; mandatory
- Retry with backoff and jitter — idempotent transient failures only
- Circuit breaker — stops hammering a degraded upstream
- Bulkhead — isolates concurrency per downstream
Operator order
timeoutinnermost — bounds each attemptretryWhennext — each retry respects the timeoutBulkheadOperator— caps in-flight concurrencyCircuitBreakerOperatoroutermost — sees the final outcomeonErrorResume(CallNotPermittedException)— fallback on open breaker
Retry rules
- Never retry 4xx
- Never retry non-idempotent writes without an idempotency key
- Always include jitter — synchronized retries amplify outages
- Cap attempts (3 typical) and total budget
- Filter exceptions explicitly — retry only 502 / 503 / 504 / TimeoutException
Resilience4j
CircuitBreakerOperator.of(cb)andBulkheadOperator.of(bh)via.transformDeferred- Configure in YAML — sliding window, failure threshold, wait duration, half-open calls
- Exposes Micrometer metrics — alert on open-state and rejection rates
Hedged requests
Mono.firstWithSignal(call, call.delaySubscription(150ms))- ~5 percent extra calls; cuts p99 dramatically
Tuning rules
- Timeout from upstream p99 + buffer, not p50
- Per-call timeout < your own SLO
- Bulkhead size ~= target qps × p99 latency
- Circuit breaker tuned by SLO burn rate, not arbitrary thresholds
Common pitfalls
- Wrong operator order — timeout outside retry breaks per-attempt bounds
- No jitter — retry storms
- Retrying writes without idempotency keys — duplicates
- Shared breaker across endpoints — one bad endpoint trips all
- No fallback — slow failure becomes fast failure with no value
Strategic answer
- Every outbound call: timeout + jittered retry + breaker + fallback
- Idempotency keys for any retried write
- Bulkhead per downstream
- Metrics and alerts on rejection / open-state
- Tune from observed p99 and SLO burn, not guesses
Reactive Resilience — Retry, Timeout, Circuit Breaker, Bulkhead
Direct Answer
Resilience for outbound WebClient calls layers four primitives: timeout bounds latency (always set per call), retryWhen(Retry.backoff(...)) with jitter and an exception filter retries idempotent transient failures, circuit breaker (Resilience4j reactive) stops hammering a degraded upstream, and bulkhead isolates concurrency per downstream so one slow service can't drain capacity. Order matters in the operator chain — timeout innermost, then retry, then circuit breaker outermost — so retries respect timeout and the breaker counts retried failures correctly. Never retry non-idempotent writes without an idempotency key, and never retry 4xx.
Common Mistakes & Pitfalls
Retrying non-idempotent writes; placing timeout outside retry; using fixed-delay retries; no jitter; no exception filter; no fallback
Check your understanding
Answer these without looking back at the article.
- Why does timeout need to be inside retry, not outside?
- How do you size a bulkhead for a specific downstream service?
- What is a hedged request and when does it help?
- How do you safely retry a POST request to charge a payment?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.