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 -

Maven pom.xml
<dependency>
  <groupId>dev.failsafe</groupId>
  <artifactId>failsafe</artifactId>
  <version>3.3.0</version>
</dependency>
build.gradle
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.java
  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.java
  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.

on twitter to get updates on new posts.

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!