Skip to content

Simple library to manage retries on operations that may fail because of exceptions.

License

Notifications You must be signed in to change notification settings

kaaveland/tryagain

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tryagain

Micro-library to take away the tedious task of writing counting loops for retrying actions that might fail because of unstable networks or end systems. This is error-prone code that often has to be added in a rush.

Using tryagain, it looks like this:

String result = TryAgain.on(RuntimeException.class)
    .maxAttempts(2).execute(new Retriable<String>() {
    @Override
    public String execute(final int attempt) throws Exception {
        if (attempt == 1) {
            throw new RuntimeException();
        }
        return "Result";
    }
});

Maven dependency

<dependency>
  <groupId>com.github.kaaveland</groupId>
  <artifactId>tryagain</artifactId>
  <version>0.1.0</version>
</dependency>

The library throws Exception so that Retriable doesn't have to rethrow checked exceptions as something else. on() accepts multiple exceptions:

TryAgain.on(HttpResponseException.class, SocketTimeoutException.class)
    .maxAttempts(10)
    .withDelay(1000)
    .execute(saveDocument);

To invoke a void method, implement RetriableWithoutResult instead of Retriable.

TryAgain.on() vs TryAgain.onInstanceOf()

TryAgain.on matches exactly on the class of the exception, it does not use an instance-check and will never match on subclasses. If you need to retry all subclasses of IOException, you need to use TryAgain.onInstanceOf.

Avoiding checked exceptions

In order to avoid having to declare throws Exception everywhere, it is possible to configure exceptions to be wrapped in WrappedException, which is a RuntimeException:

TryAgain.on(IOException.class)
    .maxAttempts(2).wrapExceptions().execute(throwsIOException);

Or if you don't want to have to worry about breaking exception-handling further up the callstack, you can bypass checked exceptions entirely, so thrown exceptions are rethrown with no wrapping:

TryAgain.on(IOException.class)
    .maxAttempts(2).bypassExceptionChecking().execute(throwsIOException);

This allows you to surgically insert retries into code without knowing too much about the code that is calling down to you.

Use the last one responsibly, it'll be able to throw exceptions that it does not declare. If you try to catch these, javac will helpfully tell you that you have dead code. In order to convince javac that the code isn't dead, you can use .declare(IOException.class) on the object returned by bypassExceptionChecking():

TryAgain.on(IOException.class, RuntimeException.class)
    .maxAttempts(10).bypassExceptionChecking().declare(IOException.class)
    .execute(operation);

Adding delays between retries

Sometimes it is useful to have delays between attempts to invoke services over the network. .withDelay(millis) will add static a delay between retries and .exponentialBackoff(millis) will use exponential backoff to calculate delays.

Other considerations

All classes exposed through the library are immutable and should be threadsafe. Retriers can be new()ed safely and shared between objects that should have the same settings or they can be created from the factory methods on TryAgain.

About

Simple library to manage retries on operations that may fail because of exceptions.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages