Fault Tolerance 5.3
Today, we announce the release of SmallRye Fault Tolerance 5.3.0. This release includes one big new feature, the programmatic API, and several smaller additions and fixes. It should be a safe upgrade for everyone using SmallRye Fault Tolerance 5.2.1.
Programmatic API
Since the very beginning, SmallRye Fault Tolerance has implemented the MicroProfile Fault Tolerance declarative, annotation-based API. It also has several SmallRye-specific additions to this API, such as retry backoff strategies or circuit breaker maintenance.
With this release, SmallRye Fault Tolerance adds an alternative, programmatic API. It has extensive documentation, so we’ll just provide a short overview here. Let’s start with an example:
public class MyService {
private static final FaultTolerance<String> guard = FaultTolerance.<String>create()
.withCircuitBreaker().done()
.withFallback().handler(() -> "fallback").done()
.build();
public String hello() throws Exception {
return guard.call(externalService::hello);
}
}
The important part of this snippet is the guard
variable.
It contains a FaultTolerance
instance, which is basically a configured set of fault tolerance strategies.
The builder API allows creating the same fault tolerance strategies as the annotation-based API of MicroProfile Fault Tolerance.
Order of with*
method invocations doesn’t matter, the fault tolerance strategies are ordered according to the MicroProfile Fault Tolerance specification.
The FaultTolerance
interface includes methods to run a Callable
, Supplier
or Runnable
and guard them.
That’s what guard.call(...)
does in the body of the hello
method.
A FaultTolerance
instance created like this may be used to guard multiple different actions.
If you only need to guard a single action, the previous example can be shortened to:
public class MyService {
private final Callable<String> guard = FaultTolerance.createCallable(externalService::hello)
.withCircuitBreaker().done()
.withFallback().handler(() -> "fallback").done()
.build();
public String hello() throws Exception {
return guard.call();
}
}
Similarly to the set of FaultTolerance
methods to guard various types of actions, there’s a set of create*
static methods to create a Callable
, Supplier
or Runnable
. There’s also a set of createAsync*
static methods to guard asynchronous actions using CompletionStage
.
Mutiny support
The smallrye-fault-tolerance-mutiny
artifact, also described below in the section on additional async types, contains a MutinyFaultTolerance
interface.
That interface contains create*
static methods to guard asynchronous actions that return the Mutiny Uni
type.
Events
The programmatic API has one feature that the declarative API doesn’t have: ability to observe certain events. For example, when configuring a circuit breaker, it is possible to register a callback for circuit breaker state changes or for a situation when an open circuit breaker prevents an invocation. When configuring a timeout, it is possible to register a callback for when the invocation times out, etc. etc. For example:
private static final FaultTolerance<String> guard = FaultTolerance.<String>create()
.withTimeout().duration(5, ChronoUnit.SECONDS).onTimeout(() -> ...).done()
.build();
Circuit breaker maintenance
There’s one exception to the above claim that the declarative API isn’t able to observe events.
With this release, CircuitBreakerMaintenance
gains a method to observe circuit breaker state changes.
No other events are exposed to the declarative API at the moment.
Note that the programmatic API also has a method to obtain CircuitBreakerMaintenance
: FaultTolerance.circuitBreakerMaintenance()
.
This methods returns the same object that you can @Inject
, because circuit breakers created by the programmatic API and declarative API are stored in the same registry.
Inspecting exception causes
The @CircuitBreaker
, @Fallback
and @Retry
fault tolerance strategies allow declaring the set of exception types for which the strategy should apply (or be ignored).
When an exception is thrown, its class is checked for presence in one of these sets, and the strategy behaves accordingly.
This works fine, until wrapper exceptions come to play. In certain contexts, the true exceptions are often (or always) wrapped into another exception. In such situation, configuring when a fault tolerance strategy should apply or be ignored becomes nearly impossible.
In this release, SmallRye Fault Tolerance adds a non-standard feature to solve this problem. In the non-compatible mode, if the class of the thrown exception isn’t present in either of the two sets, the cause chain of the exception is inspected automatically.
Revamped async types support
SmallRye Fault Tolerance offers support for more asynchronous types than just MicroProfile Fault Tolerance mandated CompletionStage
.
So far, that support was based on the SmallRye Reactive Converters project, because that offers conversion between CompletionStage
and other types.
Asynchronous implementations of fault tolerance strategies in SmallRye Fault Tolerance are based on CompletionStage
, so that seems like a natural fit, but it has one issue.
In case the other async type is lazy (which is always the case with RxJava, Mutiny or Reactor), resubscription doesn’t work.
In this release, the dependency on SmallRye Reactive Converters is dropped. Instead, SmallRye Fault Tolerance has its own set of support libraries for various reactive types. These support libraries convert between types lazily, so resubscription works properly.
Specifically, there’s smallrye-fault-tolerance-mutiny
for Mutiny and smallrye-fault-tolerance-rxjava3
for RxJava 3. Support for RxJava 1, RxJava 2 and Reactor was dropped, but may easily be added back if there’s a need.
Others
If you use @Fallback
with a FallbackHandler
class, you might have found that such fallback handler triggers a validation error when guarding methods that declare a primitive return type (or void
).
This has been fixed.
A FallbackHandler
that declares a wrapper type (such as java.lang.Integer
) now correctly matches a corresponding primitive type (such as int
).
Upgrade to 5.3.0 is very much recommended. As described above, there are certain new things, so if you encounter any bugs, please let us know in the issue tracker! We’d be specifically interested in any feedback on the programmatic API. This is very new, so you have a unique chance to influence its future!