Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying an exception-specific backoff policy with Spring-Retry

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?

like image 658
Zim-Zam O'Pootertoot Avatar asked Sep 01 '15 05:09

Zim-Zam O'Pootertoot


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.

How do you implement spring Retry?

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.

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.

Does spring retry block thread?

Does the default spring-retry implementation block threads while retrying? The implementation in github indicates that it does.


1 Answers

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 );
like image 111
oailloud Avatar answered Sep 23 '22 17:09

oailloud