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 fallbackMethod on 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

CallGood fallbackBad fallback
Product detailsCached copy, "price may have changed"Made-up price
RecommendationsBest sellers listError 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 orderFail with a clear 503 and retry laterAssume 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