The re-retrying module provides a general purpose method for retrying arbitrary Java code with specific stop, retry, and exception handling capabilities that are enhanced by Guava's predicate matching.
This is a fork of the guava-retrying library by Ryan Holder (rholder), which is itself a fork of the RetryerBuilder by Jean-Baptiste Nizet (JB). The guava-retrying project added a Gradle build for pushing it up to Maven Central, and exponential and Fibonacci backoff WaitStrategies that might be useful for situations where more well-behaved service polling is preferred.
Why was this fork necessary? The primary reason was to make it compatible with projects using later versions of Guava. See this project's Wiki for more details.
<dependency>
<groupId>tech.huffman.re-retrying</groupId>
<artifactId>re-retrying</artifactId>
<version>3.0.0</version>
</dependency>
compile "tech.huffman.re-retrying:re-retrying:3.0.0"
Given a function that reads an integer:
public int readAnInteger() throws IOException {
...
}
The following will retry if the result of the method is zero, if an IOException
is thrown, or if any other RuntimeException
is thrown from the call()
method. It will stop after attempting to retry 3 times and throw a RetryException
that contains information about the last failed attempt. If any other Exception
pops out of the call()
method it's wrapped and rethrown in an ExecutionException
.
Retryer retryer = RetryerBuilder.newBuilder()
.retryIfResult(Predicates.equalTo(0))
.retryIfExceptionOfType(IOException.class)
.retryIfRuntimeException()
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
try {
retryer.call(this::readAnInteger);
} catch (RetryException | ExecutionException e) {
e.printStackTrace();
}
Create a Retryer
that retries forever, waiting after every failed retry in increasing exponential backoff intervals until at most 5 minutes. After 5 minutes, retry from then on in 5 minute intervals.
Retryer retryer = RetryerBuilder.newBuilder()
.retryIfExceptionOfType(IOException.class)
.retryIfRuntimeException()
.withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES))
.withStopStrategy(StopStrategies.neverStop())
.build();
You can read more about exponential backoff and the historic role it played in the development of TCP/IP in Congestion Avoidance and Control.
Create a Retryer
that retries forever, waiting after every failed retry in increasing Fibonacci backoff intervals until at most 2 minutes. After 2 minutes, retry from then on in 2 minute intervals.
Retryer retryer = RetryerBuilder.newBuilder()
.retryIfExceptionOfType(IOException.class)
.retryIfRuntimeException()
.withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES))
.withStopStrategy(StopStrategies.neverStop())
.build();
Similar to the ExponentialWaitStrategy
, the FibonacciWaitStrategy
follows a pattern of waiting an increasing amount of time after each failed attempt.
Instead of an exponential function it's (obviously) using a Fibonacci sequence to calculate the wait time.
Depending on the problem at hand, the FibonacciWaitStrategy
might perform better and lead to better throughput than the ExponentialWaitStrategy
- at least according to A Performance Comparison of Different Backoff Algorithms under Different Rebroadcast Probabilities for MANETs.
The implementation of FibonacciWaitStrategy
is using an iterative version of the Fibonacci because a (naive) recursive version will lead to a StackOverflowError at a certain point (although very unlikely with useful parameters for retrying).
Inspiration for this implementation came from Efficient retry/backoff mechanisms.
Javadoc can be found here.
The re-retrying module uses a Gradle-based build system. In the instructions below, ./gradlew
is invoked from the root of the source tree and serves as a cross-platform, self-contained bootstrap mechanism for the build. The only prerequisites are Git and JDK 1.8+.
git clone git://github.com/rhuffman/re-retrying.git
./gradlew build
./gradlew install
The re-retrying module is released under version 2.0 of the Apache License.
- Jean-Baptiste Nizet (JB)
- Jason Dunkelberger (dirkraft)
- Diwaker Gupta (diwakergupta)
- Jochen Schalanda (joschi)
- Shajahan Palayil (shasts)
- Olivier Grégoire (fror)
- Andrei Savu (andreisavu)
- (tchdp)
- (squalloser)
- Yaroslav Matveychuk (yaroslavm)
- Stephan Schroevers (Stephan202)
- Chad (voiceinsideyou)
- Kevin Conaway (kevinconaway)
- Alberto Scotto (alb-i986)
- Ryan Holder(rholder)
- Robert Huffman (rhuffman)