Getting Started with Failsafe Java lib
Every application requires handling failures and retries. Let’s look at how to do it using the FailSafe library.
Failsafe Java library provides some concise APIs for handling anything. It does so by allowing you to wrap your execution logic with policies that can handle success, failures, retries, and much more.
1. Installation
To use the Failsafe library, add the following dependency to the project -
<dependency>
<groupId>dev.failsafe</groupId>
<artifactId>failsafe</artifactId>
<version>3.3.0</version>
</dependency>
implementation 'dev.failsafe:failsafe:3.3.0'
The latest Failsafe version can be found on Maven Central.
2. What are policies?
Failsafe policies catch any failures during the execution and handle them. Policies determine what execution results or exceptions are considered processing failures and how to handle those.
Failsafe supports policies like Retry, Fallback, Timeout, Circuit Breaker, Bulkhead, and Rate Limit. These policies can be applied one-off or composed to apply more than one to the execution.
Policies can be applied to just run an execution logic or to capture the result of the execution in synchronous or asynchronous mode.
// Synchronous method call
Failsafe.with(retry)
.run(() -> someMethod());
// Asynchronous method call
CompletableFuture<Void> result = Failsafe.with(retry)
.runAsync(() -> someMethod());
//Get a value
String value = Failsafe.with(retry)
.get(() - getValue());
//Get a value with asynchronous execution
CompletableFuture<T> asyncValue = Failsafe.with(retry)
.getAsync(() -> getValue());
2.1. Using Retry Policy
The most common use case may be to handle retries. We often need to retry an execution and overcome any temporary and recoverable failures.
The Retry policy lets you retry a failed execution for a given number of times. You can add a delay between retries, the maximum number of retries or attempts, as well as any specific exception types to be handled.
By default, the Retry policy handles any exception raised by the execution. |
A simple retry policy configuration may look like the below -
RetryPolicy<String> retry = RetryPolicy.<String>builder()
.withDelay(Duration.ofSeconds(2))
.withMaxRetries(3)
.build();
The above retry policy configuration will retry the processing maximum 3 times, with a delay of 2 seconds between each execution.
Here is an example of how to use the retry policy to get a value -
String result = Failsafe.with(retry)
.get(() -> getSomeString());
2.2. Using Fallbacks
Sometimes when all retries fail, you may want to get an alternative result. In such cases, the Fallback policy can be used with the retry policy. FailSafe supports composing any number of policies.
Fallback<String> fallback = Fallback.<String>of(() -> "Get a Fallback data"); (1)
String result = Failsafe.with(fallback) (2)
.compose(retry) (3)
.get(() -> getSomeString());
1 | Define a fallback policy to return an alternate string value |
2 | Create a FailSafe with the fallback |
3 | Compose to add retry policy |
Composed policies executes last to first. In the above example, retry will be applied first and then fallback for the final result of retry. |
2.3. Using Timeout
The Timeout policy allows you to fail an execution if it takes longer than expected to complete the execution.
Timeout<Object> timeout = Timeout.builder(Duration.ofSeconds(5)).withInterrupt() (1)
.build();
String result = Failsafe.with(timeout) (2)
.compose(retry) (2)
.get(() -> getSomeString());
1 | Create a Timeout policy to interrupt the execution if it doesn’t finish in 5 seconds |
2 | Compose the timeout and retry policy |
3. Event Listeners
Sometimes it is needed to log or react to the retry attempt or other policy events. FailSafe provides event listeners that can allow you to react to a certain event such as a Retry attempt, exhausting all retries, successful execution, and more.
The event listeners can be added at the FailSafe execution level. When composing multiple policies, these listeners will execute them after applying all policies.
String result = Failsafe.with(retry)
.onFailure(e -> log.log(Level.INFO,"Failed after {0} attempts with {1}", new Object[]{e.getAttemptCount(), e.getException()})) (1)
.onSuccess(e -> log.log(Level.INFO, "Succeeded after {0} attempts with result: {1}", new Object[]{e.getAttemptCount(), e.getResult()})) (2)
.onComplete(e -> log.log(Level.INFO, "Completed Execution in {0} millis", e.getElapsedAttemptTime().toMillis())) (3)
.get(() -> getSomeString(failCounter));
Success and failure log with execution event listeners -
// A successful execution
INFO: Succeeded after 3 attempts with result: success (2)
INFO: Completed Execution in 4,054 millis (3)
// All retry exhausted
INFO: Failed after 4 attempts with java.lang.Exception: Custom Exception (1)
INFO: Completed Execution in 6,009 millis (3)
The event listener can give a lot of useful metrics regarding the execution such as the number of attempts made, time taken to complete the execution, first attempt, start time, etc.
You can also add event listeners at the individual policy levels -
RetryPolicy<String> retry = RetryPolicy.<String>builder()
.withDelay(Duration.ofSeconds(2))
.withMaxRetries(3)
.onFailedAttempt(e -> log.log(Level.INFO, "Failed Attempt {0}", e.getAttemptCount()))
.onRetry(e -> log.log(Level.INFO, "Failure# {0}. Retrying ...", e.getAttemptCount()))
.onSuccess(e -> log.log(Level.INFO, "Succeeded with result: {0}", e.getResult()))
.onRetriesExceeded(e -> log.log(Level.INFO, "Exhausted all retries"))
.build();
4. Conclusion
In this article, we learned about the FailSafe java library and where it can be used. We looked at some commonly needed policies. See failsafe.dev to learn more about the library.
Stay updated!
On this blog, I post articles about different technologies like Java, MuleSoft, and much more.
You can get updates for new Posts in your email by subscribing to JavaStreets feed here -
Lives on Java Planet, Walks on Java Streets, Read/Writes in Java, JCP member, Jakarta EE enthusiast, MuleSoft Integration Architect, MuleSoft Community Ambassador, Open Source Contributor and Supporter, also writes at Unit Testers, A Family man!