Resilience4j version: 1.3.1
Java version: 13
I register event listener for all circuit breakers like this:
// apply event listener for existing circuit breaker instances
circuitBreakerRegistry.getAllCircuitBreakers()
.forEach(cB -> cB.getEventPublisher().onEvent(this::onEvent));
// apply event listener for newly created circuit breaker instances
circuitBreakerRegistry.getEventPublisher().onEntryAdded(event -> {
event.getAddedEntry().getEventPublisher().onEvent(this::onEvent);
});
With the following code the fallback is properly called when the {method invocation that raises an exception} method is invoked.
@CircuitBreaker(name = EXECUTE_PAYMENT, fallbackMethod = "executePaymentFallback")
@Bulkhead(name = EXECUTE_PAYMENT, fallbackMethod = "executePaymentFallback")
public ResilienceResponse<DTO> executePayment(String id) {
DTO response ={method invocation that raises an exception};
return new ResilienceResponse<>(response, null);
}
Now since the fallback is called I麓d expect that an error is propagated to my onEvent listener.
But instead of that the success event type is being propagated.
My question now is. When exactly will the ERROR event type occur?
And what can I do to track all exceptions, which are thrown inside a @CircuitBreaker method?
It this maybe a bug?
When you use fallbackMethod you enable fallback at CircuitBreakerAspect. In case of exception it is handled by this fallback first and it is the reason why nothing is noticed by EventPublisher. At the end there is "no exception".
When you use annotations you don't have impact on resilience layers order in contrast to functional way where you can compose decorators in expected order:
DTO response = Decorators.ofSupplier(() -> methodThatRaisesAnException(id))
.withCircuitBreaker(circuitBreaker)
.withBulkhead(bulkhead)
.withFallback(fallback)
.decorate()
.get();
https://resilience4j.readme.io/docs#section-all-core-modules-and-the-decorators-class
Each decorator (CircuitBreaker, TimeLimiter) can have it's own fallback method. You can think of as an onion and the chain of responsibility pattern.
The Aspect (decorator) order is as follows:
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( YourMethod ) ) ) ) )
Exceptions are propagated from your method to the outside. The first fallback method which is responsible will handle the exception. (Chain of responsibility pattern)
When your Bulhead and CircuitBreaker have the same fallback method, the fallback method of the Bulkhead will handle the exception and convert it into a call which is completed successfully. Which means the CircuitBreaker won't count the call as a failure.
I think in your case it doesn't make sense to add a fallback method to the Bulkhead since you want to count certain exceptions as a failure.
Thanks a lot. This resolves our issue.
Most helpful comment
Each decorator (CircuitBreaker, TimeLimiter) can have it's own fallback method. You can think of as an onion and the chain of responsibility pattern.
The Aspect (decorator) order is as follows:
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( YourMethod ) ) ) ) )
Exceptions are propagated from your method to the outside. The first fallback method which is responsible will handle the exception. (Chain of responsibility pattern)
When your Bulhead and CircuitBreaker have the same fallback method, the fallback method of the Bulkhead will handle the exception and convert it into a call which is completed successfully. Which means the CircuitBreaker won't count the call as a failure.
I think in your case it doesn't make sense to add a fallback method to the Bulkhead since you want to count certain exceptions as a failure.