Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Recover methods are not triggered with @Retryable

I followed this question after getting ExhaustedRetryException on @Retryable function. The @Retryable function is retrying.

Here's the Delegate with the @Retryable function:

@Component
public class OrderRequestDelegate {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderRequestDelegate.class);

    private final OrderRequestDao orderRequestDao;
    private final SqsQueueDao sqsQueueDao;

    @Autowired
    public OrderRequestDelegate(OrderRequestDao orderRequestDao, SqsQueueDao sqsQueueDao) {
        this.orderRequestDao = orderRequestDao;
        this.sqsQueueDao = sqsQueueDao;
    }

    @Retryable(include=NoResultException.class, backoff = @Backoff(delay = 100, maxDelay = 101), maxAttempts = 5)
    public OrderRequest processItem(String storeId, String message) {
        Long id = 999L;
        OrderRequest result = orderRequestDao.findOne(id);
        if (result == null) {
            LOGGER.info("Tried in retryable");
            throw new NoResultException();
        }
        return result;
    }

    @Recover
    public void recover(NoResultException e, Long id) {
        LOGGER.info("recover triggered");
    }

    @Recover
    public void recover(Exception e, Long id) throws Exception {
        LOGGER.info("retry failure");
    }
}

Here's the class with the caller of the function:

@RestController
@RequestMapping("/orderRequests")
@Api(description = "orders API")
public class OrderRequestController {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderRequestController.class);

    private final OrderRequestDelegate orderRequestDelegate;

    @Autowired
    public OrderRequestController(OrderRequestDelegate orderRequestDelegate) {
        this.orderRequestDelegate = orderRequestDelegate;
    }

    //...  

    @ApiOperation(value="test function")
    @RequestMapping(method = RequestMethod.GET)
    public String get() {
        orderRequestDelegate.processItem("100", "abc");
        return String.format("Worked");
    }
}

And the Application class that includes the @EnableRetry annotation:

@SpringBootApplication
@EnableRetry
public class FulfillmentApplication extends SpringBootServletInitializer {

    private static final Logger LOGGER = LoggerFactory.getLogger(FulfillmentApplication.class);

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(FulfillmentApplication.class);
    }

    public static void main(String[] args) {
        LOGGER.debug("Starting Spring application main...");
        SpringApplication.run(FulfillmentApplication.class, args);
    }
}

Here's the stacktrace when I run the @Retryable function:

2017-01-27 12:58:21.918  INFO 93294 --- [nio-8080-exec-1] c.c.f.delegate.OrderRequestDelegate      : Tried in retryable
2017-01-27 12:58:22.022  INFO 93294 --- [nio-8080-exec-1] c.c.f.delegate.OrderRequestDelegate      : Tried in retryable
2017-01-27 12:58:22.129  INFO 93294 --- [nio-8080-exec-1] c.c.f.delegate.OrderRequestDelegate      : Tried in retryable
2017-01-27 12:58:22.231  INFO 93294 --- [nio-8080-exec-1] c.c.f.delegate.OrderRequestDelegate      : Tried in retryable
2017-01-27 12:58:22.336  INFO 93294 --- [nio-8080-exec-1] c.c.f.delegate.OrderRequestDelegate      : Tried in retryable
2017-01-27 12:58:22.352 ERROR 93294 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method; nested exception is javax.persistence.NoResultException] with root cause

javax.persistence.NoResultException: null
    at com.cfa.fulfillmentApi.delegate.OrderRequestDelegate.processItem(OrderRequestDelegate.java:44) ~[classes/:na]
    at com.cfa.fulfillmentApi.delegate.OrderRequestDelegate$$FastClassBySpringCGLIB$$f297a63a.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:74) ~[spring-retry-1.1.2.RELEASE.jar:na]
    at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:263) ~[spring-retry-1.1.2.RELEASE.jar:na]
    at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:168) ~[spring-retry-1.1.2.RELEASE.jar:na]
    at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:98) ~[spring-retry-1.1.2.RELEASE.jar:na]
    at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:118) ~[spring-retry-1.1.2.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at com.cfa.fulfillmentApi.delegate.OrderRequestDelegate$$EnhancerBySpringCGLIB$$44ec8aa9.processItem(<generated>) ~[classes/:na]
    at com.cfa.fulfillmentApi.controller.OrderRequestController.get(OrderRequestController.java:45) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_112]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_112]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_112]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_112]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:220) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) ~[spring-boot-1.4.3.RELEASE.jar:1.4.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:105) ~[spring-boot-actuator-1.4.3.RELEASE.jar:1.4.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) ~[spring-boot-actuator-1.4.3.RELEASE.jar:1.4.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_112]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_112]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_112]
like image 356
Joe Essey Avatar asked Dec 01 '22 12:12

Joe Essey


2 Answers

Looks Like you just missed the basic part like i did.

The method annotated with @Recover should take The same type of argument (and optional Throwable or its subclass typed argument). Beside that the The return type should also be the same.

Here is how your recover method should look like to be fired after the retry exhausted.

@Component
public static class Foo {

    @Retryable(include=NoResultException.class, backoff = `@Backoff(delay = 100, maxDelay = 101), maxAttempts = 5)`
    public void out(String foo) {
        System.out.println(foo);
        if (foo.equals("foo")) {
            throw new NoResultException();
        }
        else {
            throw new IllegalStateException();
        }
    }

    @Recover
    public void connectionException(NoResultException e, String foo) {
        System.out.println("Retry failure");
    }

    @Recover
    public void connectionException(Exception e, String foo) throws Exception {
        System.out.println("Retry failure");
        throw e;
    }

}
like image 157
Tadele Ayelegn Avatar answered Dec 05 '22 11:12

Tadele Ayelegn


Here's the complete app from that answer, modified to be functionally equivalent to yours; if you still can't figure out what's wrong, edit your question with complete configuration and a debug log.

@SpringBootApplication
@EnableRetry
public class So38601998Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(So38601998Application.class, args);
        Foo bean = context.getBean(Foo.class);
        try {
            bean.out("foo");
            System.out.println("Recovered");
        }
        catch (Exception e) {
            System.out.println("Not recovered: " + e);
        }
        try {
            bean.out("bar");
        }
        catch (Exception e) {
            System.out.println("Not recovered: " + e);
        }
    }


    @Component
    public static class Foo {

        @Retryable(include=NoResultException.class, backoff = @Backoff(delay = 100, maxDelay = 101), maxAttempts = 5)
        public void out(String foo) {
            System.out.println(foo);
            if (foo.equals("foo")) {
                throw new NoResultException();
            }
            else {
                throw new IllegalStateException();
            }
        }

        @Recover
        public void connectionException(NoResultException e) {
            System.out.println("Retry failure");
        }

        @Recover
        public void connectionException(Exception e) throws Exception {
            System.out.println("Retry failure");
            throw e;
        }

    }

}

Results (with DEBUG logging for o.s.retry)

14:16:25.803 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=0
foo
14:16:25.913 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=1
14:16:25.914 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=1
foo
14:16:26.017 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=2
14:16:26.017 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=2
foo
14:16:26.121 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=3
14:16:26.121 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=3
foo
14:16:26.223 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=4
14:16:26.223 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=4
foo
14:16:26.224 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=5
14:16:26.224 [main] DEBUG o.s.retry.support.RetryTemplate - Retry failed last attempt: count=5
Retry failure
Recovered
14:16:26.224 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=0
bar
14:16:26.225 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=1
14:16:26.225 [main] DEBUG o.s.retry.support.RetryTemplate - Retry failed last attempt: count=1
Retry failure
Not recovered: java.lang.IllegalStateException
like image 22
Gary Russell Avatar answered Dec 05 '22 12:12

Gary Russell