spring-cloud / spring-cloud-commons

Common classes used in different Spring Cloud implementations
Apache License 2.0
701 stars 697 forks source link

Allow optional wrapping in NoFallbackAvailableException in CircuitBreaker #1344

Open MPoorter opened 5 months ago

MPoorter commented 5 months ago

Is your feature request related to a problem? Please describe. I'm building an application with Spring Cloud 2023 using Feign and Resilience4j.

In the CircuitBreaker class, methods that do not have a fallback by default have any exceptions thrown wrapped in a NoFallbackAvailableException:

default <T> T run(Supplier<T> toRun) {
  return run(toRun, throwable -> {
    throw new NoFallbackAvailableException("No fallback available.", throwable);
  });
}

This is not behaviour that we see with the vanilla implementation of Resilience4j, which just throws the exception as it is, without wrapping it.

I noticed this behaviour because I want to ignore an exception across all circuit breakers, and so set the following property resilience4j.circuitbreaker.configs.default.ignore-exceptions=.... With the automatic wrapping, however, higher-level code should be expecting a NoFallbackAvailableException instead of the exception that's actually been marked as ignored. Additionally, higher-level circuit breakers will not be able to continue ignoring this exception. [1]

Describe the solution you'd like Would it be possible to make wrapping the exception in a NoFallbackAvailableException optional, controlled by some kind of property?

Describe alternatives you've considered We can of course catch the NoFallbackAvailableException, get the root cause and then throw that exception.

[1] I know that multiple stacked circuit breakers aren't ideal, however I would like all Feign calls to be protected by circuit breakers, which can be done with the following property spring.cloud.openfeign.circuitbreaker.enabled=true. The downside of using this property is that you have no control over the fallbacks for those automatic circuit breakers. In this particular case, I am unable to add a fallback to the feign client itself, as it is shared among different services. Because of this, I would like to add a manual circuit breaker around the code using @CircuitBreaker(name = "getX", fallbackMethod = "getFallbackX").