0

Though with other return type Spring retry fallback mechanism seems to be working fine, but when it comes to calling an API with return type void. Recover method is not getting called at all, I have tried with all possible type of method parameters and changing the return type of the fallback method.... Is thr any way to do it? I don't understand why the @CircuitBReaker annotation doesn't have a parameter to take the fallback method name passed as a value to it?

    @CircuitBreaker(maxAttempts = 3, openTimeout = 8000, resetTimeout = 15000)
    public Void callingXYZ(final Abc abc, final Cdf cdf) {
        return retryTemplate.execute(context -> {
            log.info("Retry count={} when calling ******", context.getRetryCount());
            AbcServiceImpl.this.update*****Settings(OrgId.fromString(abc.getUUID(), cdf.getData());
            return null;
        });
    }

   //Recover method that should get invoked
    @Recover
    public Void fallbackUpdate*****(Throwable e, Abc abc) throws Throwable{
        log.info("Inside fallback");
        if (e instanceof ClientException) {
            log.warn("Fallback method is called when trying to call ******** from ****** because of {}", e.getCause());

   //Rollback in the db based on the method attributes
            throw SpecificException.forTask(HttpStatus.SERVICE_UNAVAILABLE.value(),
                    Enum.NAME.getErrorCode(),
                    Enum.NAME.getMEssage(), true);
        }
        throw e;
    } 

Arnav Karforma
  • 112
  • 2
  • 12

3 Answers3

1

Have you ever annotated a method with @Transactional (or e.g. @Async) and it didn’t work? As if it was totally ignored by Spring? This is because annotations like these can’t be (at least by default) put just on any method, the following two conditions must be met to let Spring take an action:

The method visibility can’t be any other than public. The invocation must come from outside of the bean. This is due to how Spring proxying work by default (using CGLIB proxies). I won’t dive into details, because it’s a topic wide enough to write another article, but generally speaking, when you autowire a bean of type Sample, Spring in fact doesn’t provide you exactly with an instance of Sample. Instead it injects a generated proxy class which extends Sample (that’s the reason why you can’t make your spring bean classes final) and overrides its public methods to be able to add extra behaviour (like transactional support).

That’s why methods with @Transactional must be public (so Spring can easily override them) and also that’s why the invocation must come from outside (only then it may go through a proxy, Spring can’t replace this reference with a proxy reference).

Taken from https://codete.com/blog/5-common-spring-transactional-pitfalls/

Arnav Karforma
  • 112
  • 2
  • 12
0

Try using @Retryable it works for me

 <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
        <version>1.1.5.RELEASE</version>
    </dependency>

@Retryable(value = { NullPointerException.class},maxAttempts = 2)
public void retry() throws NullPointerException{
    throw new NullPointerException("something went wrong");
}

@Recover
public void recover(NullPointerException ex)
{
    System.out.println("iniside recover");
}

For more details check this link https://www.baeldung.com/spring-retry

0

I resolved the issue in my code, it had nothing to do with spring-retry project. I was unable to find the solution of my issue because of my lack of understanding in Spring implementation of AOP. As Spring's aspect is applied to a proxy surrounding the bean not the actual class, calling a method with @circuitbreaker won't work if it is called from a same class/Service.

Arnav Karforma
  • 112
  • 2
  • 12
  • If anyone faces the same issue and yet struggles with the solution do comment here I will try to explain with a code example. In sort if you are using inbuilt Spring's AOP you can get this working if you don't move all the circuitBreaker methods to a new or different service. – Arnav Karforma Feb 20 '20 at 12:10