I am using Spring-Retry for some database operations. On a SQLRecoverableException
I retry three times (this assumes that whatever is causing the exception is non-transient if it fails three times), on a SQLTransientException
I retry indefinitely (the program can't do anything without access to the database, so it may as well keep retrying until the user decides to reboot the server), and on any other exception I don't retry. I use an exponential backoff policy with a base retry of 100ms and a max retry of 30,000ms.
private static final int MAX_RECOVERABLE_RETRIES = 3;
private static final long INITIAL_INTERVAL = 100;
private static final long MAX_INTERVAL = 30 * 1000;
private static final double MULTIPLIER = 2.0;
public static RetryTemplate databaseTemplate() {
RetryTemplate template = new RetryTemplate();
ExceptionClassifierRetryPolicy retryPolicy = new ExceptionClassifierRetryPolicy();
Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<>();
NeverRetryPolicy baseException = new NeverRetryPolicy();
SimpleRetryPolicy recoverablePolicy = new SimpleRetryPolicy();
recoverablePolicy.setMaxAttempts(MAX_RECOVERABLE_RETRIES);
AlwaysRetryPolicy transientPolicy = new AlwaysRetryPolicy();
policyMap.put(Exception.class, baseException);
policyMap.put(SQLRecoverableException.class, recoverablePolicy);
policyMap.put(SQLTransientException.class, transientPolicy);
retryPolicy.setPolicyMap(policyMap);
template.setRetryPolicy(retryPolicy);
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(INITIAL_INTERVAL);
backOffPolicy.setMaxInterval(MAX_INTERVAL);
backOffPolicy.setMultiplier(MULTIPLIER);
template.setBackOffPolicy(backOffPolicy);
return template;
}
Ideally, I would like to use a fixed backoff of 100ms for all SQLRecoverableExceptions
, and only apply the exponential backoff policy to SQLTransientExceptions
. I could accomplish this with nested retries, but that will greatly increase the code complexity - given no other option I would prefer to simply apply the exponential backoff to both SQLRecoverableException
and SQLTransientException
exceptions.
Is there a way for me to apply different backoff policies to different exceptions using a single retry template?
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.
To enable spring-retry we need to put one annotation in the Spring Boot Application class. So open SpringRetryApplication class and add @EnableRetry in class level.
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.
Does the default spring-retry implementation block threads while retrying? The implementation in github indicates that it does.
Indeed, ExceptionClassifierRetryPolicy
is the way to go. I didn't manage to make it work with the policyMap
though.
Here is how I've used it:
@Component("yourRetryPolicy")
public class YourRetryPolicy extends ExceptionClassifierRetryPolicy
{
@PostConstruct
public void init()
{
final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts( 3 );
this.setExceptionClassifier( new Classifier<Throwable, RetryPolicy>()
{
@Override
public RetryPolicy classify( Throwable classifiable )
{
if ( classifiable instanceof YourException )
{
return new NeverRetryPolicy();
}
// etc...
return simpleRetryPolicy;
}
});
}
}
Then, you just have to set it on the retry template :
@Autowired
@Qualifier("yourRetryPolicy")
private YourRetryPolicy yourRetryPolicy;
//...
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy( yourRetryPolicy );
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