So, I was doing a simple spring boot project using resilience4j's tools, like @RateLimiter, @CircuitBreaker and @Retry. All was working fine, even with personalized exceptions, because they were caught within my service.
Now I want my service to simply throw exceptions, and manage the handling in a @RestControllerAdvice with @ExceptionHandler(s), instead of catching the exceptions and managing them within the service, as I see many people do it like that. It also makes sense, because that way, the service only does strictly what it needs to do, and another class can manage the exceptions.
The problem is that, from what I've seen (not much), all these people that do that don't use @CircuitBreaker too. To be more specific, the problem is that the exceptions are caught first by the Circuit Breaker's fallback method instead of the Exception Handlers.
Here's some code:
@CircuitBreaker(name = "clientCircuitBreaker", fallbackMethod = "fallbackCircuitBreaker")
@Retry(name="clientRetry")
@RateLimiter(name="clientRateLimiter", fallbackMethod = "fallbackRateLimiter")
@GetMapping
public ResponseEntity<?> getClient(@RequestParam long doc) {
return this.clientService.getClient(doc);
}
public ResponseEntity<Object> fallbackCircuitBreaker(Throwable exception) {
return this.clientService.fallbackCircuitBreaker(exception);
}
//...
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IErrorException.class)
public ResponseEntity<ErrorModel> personalizedException(IErrorException ex) {
return ResponseEntity.status(404).body(ex.getErrorModel());
}
//...
}
I've also tried changing the type of the @ExceptionHandler to a more specific one (and make it throw that exception), because that right there is an interface/abstract class, but nothing. I also tried using the configuration ignoreExceptions(..) for the Circuit Breaker in a java configuration file, ignoring 'IErrorException.class', but nothing.
Any idea? Or should I give up this line of thought, and just manage exceptions inside of the service?
Quick note: the annotations are executed in the inverse order that they appear, meaning
@RateLimiter, then@Retry, then@CircuitBreaker. The circuit breaker, therefore, is the last failsafe to catch any unexpected exceptions.Hi, I'm the one that asked the question, and I have found a temporary (and rather sus in my opinion) solution.
It goes like this:
Like I said, it solves it because, even though the circuit breaker still catches it, if it's a personalized exception, it re-throws it, and it's then caught by the exception handlers. The problem is that it does still trigger the circuit breaker, so, if it was a personalized exception, I also force a transition to closed state to "make it so nothing happened". Suspicious, as I said before.
Something of note is that I also have a
@Retrylike it shows in my controller, so the above solution worked for the circuit breaker, but it still retried a couple of times when throwing a personalized exception. To solve this, I ventured to use.ignoreExceptions(IErrorException.class)in the retry configuration, which didn't work in the circuit breaker configuration last time I chequed. But surprisingly, it worked.Thus, I reached the intended behaviour. It works just like catching my personalized exceptions in the service and returning a proper reponse, just that it's better distributed now. However, I feel like it's not the best option. So I ask: does something like
.ignoreExceptions(IErrorException.class)work for you in your circuit breaker configuration?Here are my dependencies and configs (relevant to this):