Spring / Resilience4j Interview questions
1. What is Resilience4j?
Resilience4j is a lightweight fault-tolerance library for Java 8 and above, built around functional programming rather than the class-heavy wrapper style of its predecessor, Netflix Hystrix. Instead of forcing every protected call through a dedicated command class, it decorates plain Supplier , F...
2. What are the core modules of Resilience4j?
Resilience4j is organized into a handful of focused modules, each one implementing a single resilience pattern that can be added to a project independently. Module Purpose CircuitBreaker Stops calling a failing dependency for a cooldown period. Retry Re-executes a failed call a bounded number of ...
3. What is a Circuit Breaker in Resilience4j?
A CircuitBreaker wraps a call to a potentially failing dependency and stops sending traffic to it once failures cross a configured threshold, instead of letting every caller keep waiting on a service that's already struggling. It works like an electrical circuit breaker: normal traffic flows thro...
4. What are the states of a CircuitBreaker?
A Resilience4j CircuitBreaker moves through several distinct states rather than just "on" or "off". CLOSED - calls pass through normally and results feed the failure-rate calculation. OPEN - calls are rejected immediately without touching the dependency. HALF_OPEN - a limited number of trial call...
5. What is the Retry module used for?
The Retry module re-executes a call that failed or returned an undesirable result, up to a configured number of attempts, instead of surfacing a transient failure straight to the caller. It's configured with a maxAttempts count and a waitDuration (or an IntervalFunction for backoff) between attem...
6. What is a RateLimiter in Resilience4j?
A RateLimiter caps how many calls are allowed to proceed within a fixed time window, rejecting or delaying calls beyond that limit rather than letting demand spike freely against a dependency. It's configured with limitForPeriod (how many calls are permitted per window), limitRefreshPeriod (how l...
7. What is the Bulkhead pattern in Resilience4j?
Bulkhead limits how many calls to a specific dependency can run at the same time, so one slow or misbehaving dependency can't consume every available thread and starve unrelated calls. The name comes from ship design, where watertight compartments (bulkheads) stop a single hull breach from sinkin...
8. What is a TimeLimiter in Resilience4j?
TimeLimiter enforces a maximum duration on an asynchronous call, throwing a TimeoutException if the call hasn't completed once the configured timeoutDuration passes. It's designed to work with CompletionStage or Future -returning calls rather than plain synchronous ones, since it needs an async h...
9. What is the Cache module in Resilience4j?
The Cache module wraps a function call so that a previously computed result for the same input is returned directly, skipping the actual call entirely on a cache hit. It's built on top of the JSR-107 (JCache) specification, so it needs an actual JCache provider - such as Ehcache - configured unde...
10. How do you add Resilience4j to a Spring Boot project?
Add the Spring Boot starter for the modules you need - typically io.github.resilience4j:resilience4j-spring-boot3 for Spring Boot 3, plus Spring's own spring-boot-starter-aop , since Resilience4j's annotations rely on Spring AOP proxies to intercept calls.
11. What is the purpose of the Decorators class?
The Decorators class provides a fluent builder for composing multiple resilience patterns around a single call, without hand-nesting one decorator function inside another. Instead of writing CircuitBreaker.decorateSupplier(cb, RateLimiter.decorateSupplier(rl, supplier)) by hand, you write Decorat...
12. Define fallback method in Resilience4j?
A fallback is an alternate code path that runs when the primary, resilience-wrapped call fails or is rejected, so the caller gets a usable result instead of a raw exception. With the functional API this is done via Try.of(supplier).recover(throwable -> fallbackValue) or Decorators...withFallback(...
13. What are the types of Bulkhead implementations?
Resilience4j offers two Bulkhead implementations, and choosing between them matters more than most other configuration decisions in the module. SemaphoreBulkhead ThreadPoolBulkhead Limits concurrent calls using a semaphore on the caller's own thread. Runs calls on a separate, bounded thread pool ...
14. List the key configuration properties of a CircuitBreaker?
failureRateThreshold - percentage of failed calls that trips the breaker to OPEN. slowCallRateThreshold / slowCallDurationThreshold - treat calls slower than a duration as failures too. slidingWindowType / slidingWindowSize - COUNT_BASED or TIME_BASED window used to compute the rate. minimumNumbe...
15. How do you apply @CircuitBreaker in a Spring Boot service?
Annotate the method that makes the risky call with @CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback") , and Spring AOP wraps it in a proxy that routes the call through the named CircuitBreaker instance. @CircuitBreaker (name = "paymentService" , fallbackMethod = "payment...
16. Why is Resilience4j often preferred over Hystrix?
Hystrix was put into maintenance mode by Netflix in 2018, so Resilience4j is preferred today largely because it's the actively maintained option, with regular releases and support for current Java versions and frameworks that Hystrix never received. Architecturally, Resilience4j is built on plain...
17. How does the sliding window work in CircuitBreaker?
The sliding window is the pool of most recent call outcomes the CircuitBreaker uses to calculate its failure rate and slow-call rate, rather than looking at the entire call history since startup. In COUNT_BASED mode, the window holds a fixed number of the most recent calls - for example, the last...
18. What is the difference between COUNT_BASED and TIME_BASED sliding windows?
COUNT_BASED TIME_BASED Window holds a fixed number of the last N calls. Window holds calls from the last N seconds. Rate stays stable regardless of traffic bursts. Rate can shift quickly during traffic spikes or lulls. Simple to reason about for steady, predictable traffic. Better reflects real-t...
19. How is failure rate calculated in a CircuitBreaker?
The failure rate is the percentage of calls in the current sliding window that either threw a recorded exception or exceeded the slow-call duration threshold, measured once enough calls have accumulated to satisfy minimumNumberOfCalls . Concretely: if the window holds 100 calls and 40 of them fai...
20. What is the difference between CircuitBreaker and Bulkhead?
CircuitBreaker and Bulkhead solve different problems even though both protect calls to a dependency: CircuitBreaker reacts to a dependency already failing or running slow, while Bulkhead limits how many calls can run concurrently regardless of whether they're failing at all. A CircuitBreaker look...
21. How does the half-open state behave in a CircuitBreaker?
HALF_OPEN is a probationary state the breaker enters after waitDurationInOpenState elapses, meant to test whether the dependency has actually recovered before fully reopening traffic. Only permittedNumberOfCallsInHalfOpenState trial calls are allowed through; every other call is still rejected ex...
22. Why do we use recordExceptions versus ignoreExceptions?
Both control which thrown exceptions the CircuitBreaker counts as a failure, but they express the intent from opposite directions and are meant to be used situationally, not both at once for the same exception. recordExceptions is an allow-list: only the listed exception types (and their subclass...
23. When should you use RateLimiter instead of Bulkhead?
Use RateLimiter when the constraint is a rate over time - calls per second or per minute - such as respecting a third-party API's documented rate limit or protecting a downstream system from a traffic burst. Use Bulkhead instead when the constraint is concurrency at any given instant - how many c...
24. What happens when a CircuitBreaker transitions to OPEN?
The moment the failure or slow-call rate crosses its threshold, the breaker immediately switches to OPEN and every subsequent call is rejected with a CallNotPermittedException instead of being attempted against the dependency at all. Any fallback method configured alongside the CircuitBreaker sti...
25. How is Resilience4j's Retry different from a plain retry loop?
A hand-rolled retry loop usually just wraps a call in a for-loop with a try/catch and a fixed sleep, which works but scatters retry logic across the codebase and makes it hard to reason about consistently. Resilience4j's Retry centralizes that behavior in a named, reusable Retry instance with con...
26. Explain the execution flow of a call decorated with CircuitBreaker, Retry, and Bulkhead together?
When these three are composed, the order they're applied in determines which one "sees" the call first, and getting that order wrong changes the actual behavior even though the code looks similar either way. The generally recommended order is Bulkhead (outermost) → CircuitBreaker → Re...
27. How do you combine multiple Resilience4j decorators using the Decorators class?
The Decorators class chains .withX() calls onto a base Supplier , Function , or similar functional type, then materializes the whole composition with .decorate() . Supplier
28. Which is better and why: SemaphoreBulkhead or ThreadPoolBulkhead?
Neither is universally "better" - they suit different situations, and the choice mostly comes down to whether the protected call is synchronous/blocking or needs true interruption on timeout. SemaphoreBulkhead is lighter weight: it limits concurrency using a plain semaphore on the caller's own th...
29. How can you optimize CircuitBreaker configuration for a high-throughput service?
On a high-throughput service, a COUNT_BASED sliding window with a reasonably large size (for example, 200-1000 calls) usually gives a more stable failure-rate signal than TIME_BASED, since traffic volume is already consistent enough that a count window won't lag behind reality. Set minimumNumberO...
30. How do you troubleshoot a CircuitBreaker that never opens despite failures?
First check minimumNumberOfCalls against actual traffic - if it's set higher than the real call volume in the current sliding window, the failure rate is never even evaluated, so the breaker can't open no matter how bad things get. Next verify the exception being thrown is actually being recorded...
31. Explain the lifecycle of a CircuitBreaker instance?
A CircuitBreaker instance is created by name from a CircuitBreakerRegistry - either the default shared registry or one built from custom CircuitBreakerConfig - and it starts in the CLOSED state with an empty sliding window. As calls flow through it, each outcome (success, failure, or slow call) i...
32. What is the difference between TimeLimiter and a plain timeout?
A plain, hand-written timeout - like calling future.get(5, TimeUnit.SECONDS) directly - throws when the deadline passes, but the underlying task usually keeps running on its own thread unless something else explicitly cancels it, which can silently leak resources over time. Resilience4j's TimeLim...
33. How does RegistryEventConsumer work in Resilience4j?
RegistryEventConsumer lets code react to changes at the registry level - when a CircuitBreaker (or Retry, Bulkhead, etc.) instance is added, removed, or replaced in its registry - rather than reacting to individual call events on one specific instance. It's implemented by overriding onEntryAddedE...
34. Why does Resilience4j rely on functional interfaces like Supplier and Function?
Building around Supplier , Function , Runnable , Callable , and CompletionStage means Resilience4j can decorate any piece of existing code that already fits one of those shapes, without requiring it to extend a base class or implement a library-specific command interface the way Hystrix's Hystrix...
35. How do you monitor Resilience4j metrics with Micrometer?
The resilience4j-micrometer module exposes each registry's metrics by binding it directly to a Micrometer MeterRegistry , after which Micrometer's usual exporters (Prometheus, Datadog, CloudWatch, etc.) pick the data up like any other application metric. TaggedCircuitBreakerMetrics .ofCircuitBrea...
36. What is the difference between waitDurationInOpenState and permittedNumberOfCallsInHalfOpenState?
These two settings control different phases of recovery and are easy to conflate since they both govern "what happens after the breaker opens". waitDurationInOpenState is a time value - how long the breaker stays fully OPEN, rejecting every call, before it's even eligible to move to HALF_OPEN and...
37. How do you write a unit test for a CircuitBreaker-protected method?
Build the CircuitBreaker from an explicit, test-specific CircuitBreakerConfig rather than relying on the application's real application.yml values, so the test controls thresholds directly instead of depending on production configuration that might change independently. CircuitBreakerConfig confi...
38. Explain the internal working of exponential backoff in the Retry module?
Exponential backoff increases the wait time between successive retry attempts rather than using the same fixed delay every time, which spreads out repeated load on a struggling dependency instead of hammering it at a constant rate. Resilience4j implements this through the IntervalFunction abstrac...
39. What is the difference between CallNotPermittedException and other Resilience4j exceptions?
Exception Thrown by / meaning CallNotPermittedException CircuitBreaker is OPEN; the call was never attempted. BulkheadFullException Bulkhead concurrency/queue limit reached; call rejected before running. RequestNotPermitted RateLimiter's permit timeout expired; call was never attempted. TimeoutEx...
40. How do you use event publishers to log CircuitBreaker state transitions?
circuitBreaker.getEventPublisher() .onStateTransition(event -> log.info("CB '{}' transitioned {} -> {}", event.getCircuitBreakerName(), event.getStateTransition().getFromState(), event.getStateTransition().getToState())) .onCallNotPermitted(event -> log.warn("Call rejected: CB '{}' is OPEN", even...
41. When would you choose a RateLimiter over a Bulkhead for protecting a downstream call?
Choose RateLimiter when the actual constraint you're protecting against is expressed as a rate - a third-party API that documents "100 requests per minute", or a downstream system whose own capacity planning is expressed in requests-per-second, not in how many requests can be in flight at once. B...
42. How does Resilience4j integrate with reactive streams in WebFlux?
The resilience4j-reactor module supplies operators - CircuitBreakerOperator , RetryOperator , RateLimiterOperator , BulkheadOperator , TimeLimiterOperator - that plug into a Mono or Flux pipeline via .transformDeferred(...) , rather than wrapping a blocking Supplier the way the core modules do. M...
43. What is the difference between recordException and recordResult predicates?
A recordExceptionPredicate decides, based on the exception a call threw, whether that particular exception should count as a failure - useful when the exception type alone (say, a checked wrapper) doesn't tell the whole story and the predicate needs to inspect the exception's cause or message. A ...
44. Explain the internal working of automaticTransitionFromOpenToHalfOpenEnabled?
By default this flag is false, which means the CircuitBreaker doesn't proactively schedule a background timer to flip itself from OPEN to HALF_OPEN - instead, the transition is evaluated lazily, checked only when the next call actually arrives after waitDurationInOpenState has elapsed. Setting it...
45. How do you configure Resilience4j using application.yml in Spring Boot?
resilience4j: circuitbreaker: instances: paymentService: slidingWindowSize: 20 failureRateThreshold: 50 waitDurationInOpenState: 10s permittedNumberOfCallsInHalfOpenState: 5 retry: instances: paymentService: maxAttempts: 3 waitDuration: 500ms bulkhead: instances: paymentService: maxConcurrentCall...
46. What is the difference between the Resilience4j Cache module and a plain in-memory cache like Guava?
Guava's Cache is a general-purpose, standalone in-memory cache: you put and get arbitrary key-value pairs directly, with eviction policies like size limits or expiry, entirely independent of any resilience concerns. Resilience4j's Cache module is narrower by design - it's a decorator specifically...
47. How can you optimize retry strategies to avoid retry storms?
A retry storm happens when many clients (or many concurrent requests on one client) all retry a failing dependency at roughly the same moments, effectively multiplying the load on a system that's already struggling and making recovery harder instead of easier. Use IntervalFunction.ofExponentialRa...
48. Explain the execution flow when a fallback method itself throws an exception?
If the fallback method itself throws, Resilience4j doesn't retry the fallback or fall back to a "fallback of the fallback" automatically - the exception thrown by the fallback simply propagates to the caller as-is, since fallback logic is treated as ordinary application code once control reaches ...
49. How do you troubleshoot memory growth caused by an unbounded ThreadPoolBulkhead queue?
Check the configured queueCapacity first - if it's left very large or effectively unbounded while the downstream dependency is slow, incoming calls pile up in the queue faster than the pool can drain them, and each queued task holds onto memory (its captured state, any request payload) until it's...
50. Explain how Resilience4j's modular design influences microservice resilience architecture at scale?
Because each pattern - CircuitBreaker, Retry, Bulkhead, RateLimiter, TimeLimiter, Cache - lives in its own module, teams can adopt resilience incrementally rather than needing to buy into a heavyweight, all-or-nothing framework before getting any protection at all; a service can start with just C...