In my previous post about Retry with Spring I - Number of Retries I covered how we can retry an operation n number of times in case the operation causes an exception.

In this post I am going to cover the simplest retry policy - TimeoutRetryPolicy. This policy keep retrying an operation until the timeout occurs. A sample use case for it might be: we just restarted the database and the database takes a few seconds to come up. So keep retrying for configured amount of seconds.

Easier to show with an example:

import com.rackspace.monitoring.BaseLoggingThing  
import org.springframework.retry.RetryCallback  
import org.springframework.retry.RetryContext  
import org.springframework.retry.policy.TimeoutRetryPolicy  
import org.springframework.retry.support.RetryTemplate

class TimeoutRetryExample extends BaseLoggingThing {  
    RetryTemplate retryTemplate = new RetryTemplate()
    CarRepository carRepository

    TimeoutRetryExample(com.rackspace.monitoring.atomhopper.CarRepository carRepository, int timeoutInSecs) {
        this.carRepository = carRepository
        TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy()
        timeoutRetryPolicy.timeout = timeoutInSecs * 1000L
        retryTemplate.retryPolicy = timeoutRetryPolicy
    }

    String getCar(final String make) {
        return retryTemplate.execute(new RetryCallback() {
            @Override
            String doWithRetry(RetryContext retryContext) throws Exception {
                // A long operation is simulated here.
                sleep(1000)
                return carRepository.findCarNameByMake(make)
            }
        })
    }
}

And corresponding test:

import spock.lang.Specification
import spock.lang.Timeout
import spock.lang.Unroll

class TimeoutRetryExampleTest extends Specification {

    CarRepository carRepository = Mock()

    @Timeout(2)
    @Unroll
    def 'retries 1 times'() {
        given:
        // Retry for 1 seconds
        TimeoutRetryExample retryExample = new TimeoutRetryExample(carRepository, 1)

        when:
        retryExample.getCar('Toyota')

        then:
        thrown(IllegalStateException)
        1 * carRepository.findCarNameByMake(_) >> { throw new IllegalStateException('foo') }
    }

    @Timeout(2)
    @Unroll
    def 'does not retry if no exception is thrown'() {
        given:
        // Retry for 1 seconds
        TimeoutRetryExample retryExample = new TimeoutRetryExample(carRepository, 5)

        when:
        def car = retryExample.getCar('Toyota')

        then:
        car == 'Family Car'
        1 * carRepository.findCarNameByMake(_) >> 'Family Car'
    }

    @Timeout(6)
    @Unroll
    def 'retries 5 times'() {
        given:
        // Retry for 5 seconds
        TimeoutRetryExample retryExample = new TimeoutRetryExample(carRepository, 5)

        when:
        def car = retryExample.getCar('Toyota')

        then:
        car == 'Camry'
        5 * carRepository.findCarNameByMake(_) >>
            { throw new IllegalStateException('foo') } >>
            { throw new IllegalStateException('foo') } >>
            { throw new IllegalStateException('foo') } >>
            { throw new IllegalStateException('foo') } >>
            'Camry'
    }
}