Reusable Fault Tolerance

This is an additional feature of SmallRye Fault Tolerance and is not specified by MicroProfile Fault Tolerance.

The declarative, annotation-based API of MicroProfile Fault Tolerance doesn’t allow sharing configuration of fault tolerance strategies across multiple classes. In a single class, the configuration may be shared across all methods by putting the annotations on the class instead of individual methods, but even then, stateful fault tolerance strategies are not shared. Each method has its own bulkhead, circuit breaker and/or rate limit, which is often not what you want.

The programmatic API of SmallRye Fault Tolerance allows using a single FaultTolerance object to guard multiple disparate actions, which allows reuse and state sharing. It is possible to use a programmatically constructed FaultTolerance object declaratively, using the @ApplyFaultTolerance annotation.

To be able to do that, we need a bean of type FaultTolerance with the @Identifier qualifier:

@ApplicationScoped
public class PreconfiguredFaultTolerance {
    @Produces
    @Identifier("my-fault-tolerance")
    public static final FaultTolerance<String> FT = FaultTolerance.<String>create()
            .withRetry().maxRetries(2).done()
            .withFallback().handler(() -> "fallback").done()
            .build();
}

See the programmatic API documentation for more information about creating the FaultTolerance instance.

It is customary to create the bean by declaring a static producer field, just like in the previous example.

Once we have that, we can apply my-fault-tolerance to synchronous methods that return String:

@ApplicationScoped
public class MyService {
    @ApplyFaultTolerance("my-fault-tolerance")
    public String doSomething() {
        ...
    }
}

It is also possible to create a bean of type FaultTolerance<Object> and apply it to synchronous methods that return different types. Note that this effectively precludes defining a useful fallback, because fallback can only be defined when the value type is known.

It is also possible to define a bean of type FaultTolerance<CompletionStage<T>> and apply it to asynchronous methods that return CompletionStage<T>. Likewise, it is possible to do this for additional asynchronous types.

Note that you can’t define a synchronous FaultTolerance<T> object and apply it to any asynchronous method. Similarly, you can’t define an asynchronous FaultTolerance<CompletionStage<T>> and apply it to a synchronous method or an asynchronous method with different asynchronous type. This limitation will be lifted in the future.

Metrics

Methods annotated @ApplyFaultTolerance gather metrics similarly to methods annotated with MicroProfile Fault Tolerance annotations. That is, each method gets its own metrics, with the method tag being <fully qualified class name>.<method name>.

At the same time, state is still shared. All methods annotated @ApplyFaultTolerance share the same bulkhead, circuit breaker and/or rate limit.

If the FaultTolerance object used for @ApplyFaultTolerance is also used programmatically, that usage is coalesced in metrics under the description as the method tag.