Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Retry does not work on 2nd level of methods

Tags:

java

spring

@Retryable doesn't seem to be working on 2nd level of methods as in sphRemoteCall below. I see that a proxy is created but it is never retried on failures.

Once I moved @Retryable to the 1st level of methods like getSubscriberAccount, it's started working.

Example below:

@Service
public class SphIptvClient extends WebServiceGatewaySupport {
    //Works over here
    @Retryable(maxAttempts=3, backoff=@Backoff(delay=100))
    public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {

        GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
        return response;
    }

    /*
     * Retryable is not working on the 2nd level methods in the bean. 
     * It works only with methods which are called directly from outside
     * if there is 2nd level method, like this, Retryable is not working.
     */
    //@Retryable
    private Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
        log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
        return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
    }
}

@Configuration
@EnableRetry
public class SphClientConfig {
    @Bean
    public SphIptvClient sphIptvClient() {
        SphIptvClient client = new SphIptvClient();
        return client;
    }
}
like image 647
Subra M Avatar asked Apr 12 '16 22:04

Subra M


People also ask

How do I enable retry in spring boot?

First, you need to enable Spring Retry. You can achieve this by adding the @EnableRetry annotation to your @SpringBootApplication or @Configuration class. You can now use @Retryable to annotate any method to be a candidate or retry and @Recover to specify fallback methods.

What is the use of spring Retry?

Spring Retry provides an ability to automatically re-invoke a failed operation. This is helpful where the errors may be transient (like a momentary network glitch).

Which tool will provide declarative retry support via spring Retry?

Spring Retry library (https://github.com/spring-projects/spring-retry) provides a declarative support to retry failed operations as well as fallback mechanism in case all the attempts fail.

What is backoff in spring Retry?

public interface BackOffPolicy. Strategy interface to control back off between attempts in a single retry operation . Implementations are expected to be thread-safe and should be designed for concurrent access.


1 Answers

So this is a super late answer, but since I've just come here and confronted the same problem (again, after years ago wrestling with transactions) I'll furnish a little more fleshed out solution and hopefully someone will find it useful. Suffice to say that @M. Deinum's diagnosis is correct.

enter image description here

enter image description here

In the above case, and to paraphrase Understanding AOP proxies, any place where SphIptvClient gets autowired will be given a reference to a proxy which Spring Retry will create when @EnableRetry is handled:

"The @EnableRetry annotation creates proxies for @Retryable beans" - Declarative Retry - Spring Retry

Once getSubscriberAccount has been invoked and execution has passed through the proxy and into the @Service instance of the object, no reference to the proxy is known. As a result sphRemoteCall is called as if there were no @Retryable at all.

You could work with the framework by shuffling code around in such a way as to allow getSubscriberAccount to call a proxy-ed sphRemoteCall, which requires a new interface and class implementation.

For example:

public interface SphWebService {
   Object sphRemoteCall(String uri, Object requestPayload, String soapAction);
}

@Component
public class SphWebServiceImpl implements SphWebService {
   @Retryable
   public Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
       log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
       return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
   }
}

@Service
public class SphIptvClient extends WebServiceGatewaySupport {

   @Autowired
   SphWebService sphWebService;

   @Retryable(maxAttempts=3, backoff=@Backoff(delay=100))
   public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {
       GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) this.sphWebService.sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
       return response;
   }
}

@Configuration
@EnableRetry
public class SphClientConfig {
   // the @Bean method was unnecessary and may cause confusion. 
   // @Service was already instantiating SphIptvClient behind the scenes.
}
like image 119
Jon Sampson Avatar answered Oct 07 '22 15:10

Jon Sampson