Careeroza
Fallbacks and Combining Patterns
Order of the Decorators
When you put several annotations on one method, Resilience4j applies them in a fixed order (not the order you write them):
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( your method ) ) ) ) ) outermost innermost
- Each retry attempt goes through the circuit breaker, so failed attempts are counted.
- When the circuit is OPEN, Retry sees
CallNotPermittedException; ignore it in the retry config so you do not retry against an open circuit. - The
fallbackMethodon the outermost annotation runs once, after everything inside has failed.
Tip: You can change the order with resilience4j.retry.retry-aspect-order, resilience4j.circuitbreaker.circuit-breaker-aspect-order etc. (higher number = higher priority). Most teams keep the default.
Good Fallbacks
| Call | Good fallback | Bad fallback |
|---|---|---|
| Product details | Cached copy, "price may have changed" | Made-up price |
| Recommendations | Best sellers list | Error page |
| Delivery estimate | "Delivery in 3–5 days" | Hide the Buy button |
| Payment | "Payment pending, we will confirm by SMS" | Mark the order PAID |
| Stock check during order | Fail with a clear 503 and retry later | Assume in stock |
Note: A fallback that throws a clear exception is also a valid fallback. Hiding a real failure can be worse than showing it.
Exercise
Exercise: Write the Resilience4j config for userService: time-based window of the last 30 seconds, open at 40% failures, at least 10 calls, stay open 20 s, 5 trial calls; retry 3 attempts with 300 ms fixed wait on IOException only.
Show answer
resilience4j:
circuitbreaker:
instances:
userService:
sliding-window-type: TIME_BASED
sliding-window-size: 30 # seconds for TIME_BASED
minimum-number-of-calls: 10
failure-rate-threshold: 40
wait-duration-in-open-state: 20s
permitted-number-of-calls-in-half-open-state: 5
retry:
instances:
userService:
max-attempts: 3
wait-duration: 300ms
retry-exceptions:
- java.io.IOException