Failsafe, a zero-dependency Java library for handling failures, has released version 2.0 with support for resilience policy composition and a pluggable architecture that enables custom policy service providers. The minimum version of Java required to run Failsafe 2.0 is Java 8.
The library provides a fluent and flexible API to handle failures in Java applications. This is done by wrapping executable code blocks inside composable resilience policies. According to the release notes, version 2.0 brings in improvements, behavioral changes, API changes, and API additions.
There are three main resilience policies provided by Failsafe: a retry policy, a circuit-breaker policy, and a fallback policy.
The retry policy defines the failure scenarios under which an executable code block should be retried. The API provides a number of configuration options like maximum number of retry attempts allowed, wait period between attempts, and exponential backoff that allows the developer to fine-tune the retry policy.
The following snippet shows a retry policy that is configured to retry three times before giving up and waiting two seconds between retries.
RetryPolicy<Object> retryPolicy = new RetryPolicy<>() .handle(SocketException.class) .withDelay(Duration.ofSeconds(2)) .withMaxRetries(3); Failsafe.with(retryPolicy).run(() -> connect());
The circuit breaker policy defines a set of thresholds that when exceeded will halt the execution of a code block. This will help systems to fail fast and prevent cascading failures and system overload. The circuit breaker toggles between three states: closed, open and half-open.
It starts in a closed state where the wrapped code block is executed as normal. When execution failures reach a preconfigured threshold, the circuit breaker transitions to an open state. Executions are halted and further attempts will fail with CircuitBreakerOpenException. Once a specified amount of time has passed in the open state, the circuit is half-opened and a few executions are allowed to determine whether the circuit should be closed or opened again.
The circuit breaker also provides metrics that show the number of successes and failures recorded in the current state.
The following snippet shows a circuit breaker that is configured to open when five successive executions have failed, wait for one minute before before attempting to close again, and close when two successive executions succeed.
CircuitBreaker<Object> circuitBreaker = new CircuitBreaker<>() .handle(SocketException.class) .withFailureThreshold(5) .withSuccessThreshold(2) .withDelay(Duration.ofMinutes(1)); Failsafe.with(circuitBreaker).run(() -> connect());
The fallback policy describes an alternate execution that should occur when a failure occurs. This policy can be used to suppress exceptions and provide a default result or throw custom exceptions.
The following snippet shows a fallback policy that is configured to execute a different method when a failure occurs.
Fallback<Object> fallback = Fallback.of(this::connectToLocal);
All available resilience policies can be combined in suitable ways to compose new policies. A typical composition uses a fallback policy as the outermost policy and a circuit-breaker policy as the innermost policy with the retry policy sitting in between the two as shown below:
Failsafe.with(fallbackPolicy, retryPolicy, circuitBreaker);
In addition to these built-in policies, Failsafe 2.0 provides a policy SPI that can be used to plug custom policies. The library provides a few additional features like configurable execution schedulers, event listeners, asynchronous execution support, and execution tracking.
The current version is Failsafe 2.0.1, which was released on GitHub last month.