I am trying spring retry and I am facing a strange issue. When I use the retry annotation on a method within a Rest Controller, the retry does not work. But if I move that method to a separate service class, it works. The following code does not work:
@RestController
public class HelloController {
@RequestMapping(value = "/hello")
public String hello() {
return getInfo();
}
@Retryable(RuntimeException.class)
public String getInfo() {
Random random = new Random();
int r = random.nextInt(2);
if (r == 1) {
throw new RuntimeException();
} else {
return "Success";
}
}
}
But the following does:
@RestController
public class HelloController {
@Autowired
private SomeService service;
@RequestMapping(value = "/hello")
public String hello() {
String result = service.getInfo();
return result;
}
}
@Service
public class SomeService {
@Retryable(RuntimeException.class)
public String getInfo() {
Random random = new Random();
int r = random.nextInt(2);
if (r == 1) {
throw new RuntimeException();
} else {
return "Success";
}
}
}
My question is why the @Retryable
is not working when used in the controller?
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.
Does the default spring-retry implementation block threads while retrying? The implementation in github indicates that it does.
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.
Spring Retry provides the ability to automatically re-invoke a failed operation. This is helpful when errors may be transient in nature. For example, a momentary network glitch, network outage, server down, or deadlock. You can configure the. spring-retry.
The issue you are seeing is due to how you are calling your getInfo()
method.
In the first example, you are calling getInfo()
from within the same spring managed bean. In the second example you are calling getInfo()
from a different spring managed bean. This distinction is subtle, but very important, and it is very likely what is causing your issues.
When you use the @Retryable
annotation, Spring is creating a proxy around your original bean so that they can do special handling in special circumstances. In this specific case, Spring applies an Advice that will delegate a call to your actual method, catching RuntimeException
's that it may throw, and retrying the invocation of your method according to the configuration of your @Retryable
annotation.
The reason this proxy matters in your case is that only external callers see the proxy advice. Your bean has no knowledge that it is proxied, and only knows that its methods are being called (by the proxy advice). When your bean calls a method on itself, no further proxying is involved, which is why the retrying does not actually occur.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With