Skip to content

Commit

Permalink
introduce a second version of the programmatic API
Browse files Browse the repository at this point in the history
This includes the `Guard` and `TypedGuard` types, together with
the `@ApplyGuard` interceptor binding annotation. These types unify
synchronous and asynchronous invocations, using the new fault tolerance
strategies introduced earlier.

The `Guard` and `TypedGuard` interfaces, as well as their implementations,
currently experience rather heavy code duplication; this can be solved
in the future (probably by generating the code from a template).

The first version of the programmatic API (`FaultTolerance` and
`@ApplyFaultTolerance`) continues to exist, but is deprecated for removal.
It will be removed in SmallRye Fault Tolerance 7.0.
  • Loading branch information
Ladicek committed Nov 18, 2024
1 parent b1f9b35 commit 2c72065
Show file tree
Hide file tree
Showing 140 changed files with 5,616 additions and 1,895 deletions.
6 changes: 6 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,11 @@
<groupId>io.smallrye.common</groupId>
<artifactId>smallrye-common-annotation</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,9 @@
import io.smallrye.common.annotation.Experimental;

/**
* A special interceptor binding annotation to apply preconfigured fault tolerance.
* If {@code @ApplyFaultTolerance("<identifier>")} is present on a business method,
* then a bean of type {@link FaultTolerance} with qualifier
* {@link io.smallrye.common.annotation.Identifier @Identifier("&lt;identifier>")}
* must exist. Such bean serves as a preconfigured set of fault tolerance strategies
* and is used to guard invocations of the annotated business method(s).
* <p>
* It is customary to create such bean by declaring a {@code static} producer field.
* That removes all scoping concerns, because only one instance ever exists. Using
* a non-static producer field or a producer method means that scoping must be carefully
* considered, especially if stateful fault tolerance strategies are configured.
* <p>
* The {@code @ApplyFaultTolerance} annotation may also be present on a bean class,
* in which case it applies to all business methods declared by the class. If the
* annotation is present both on the method and the class declaring the method,
* the one on the method takes precedence.
* <p>
* When {@code @ApplyFaultTolerance} applies to a business method, all other fault tolerance
* annotations that would otherwise also apply to that method are ignored.
* <p>
* A single preconfigured fault tolerance can be applied to multiple methods, as long as asynchrony
* of all those methods is the same as the asynchrony of the fault tolerance instance. For example,
* if the fault tolerance instance is created using {@code FaultTolerance.create()}, it can be
* applied to all synchronous methods, but not to any asynchronous method. If the fault tolerance
* instance is created using {@code FaultTolerance.createAsync()}, it can be applied to all
* asynchronous methods that return {@code CompletionStage}, but not to synchronous methods or
* asynchronous methods that return any other asynchronous type.
* <p>
* A single preconfigured fault tolerance can even be applied to multiple methods with different
* return types, as long as the constraint on method asynchrony described above is obeyed. In such
* case, it is customary to declare the fault tolerance instance as {@code FaultTolerance<Object>}
* for synchronous methods, {@code FaultTolerance<CompletionStage<Object>>} for asynchronous
* methods that return {@code CompletionStage}, and so on. Note that this effectively precludes
* defining a useful fallback, because fallback can only be defined when the value type is known.
* <p>
* Note that this annotation has the same differences to the standard MicroProfile Fault Tolerance
* as {@link FaultTolerance}:
* <ul>
* <li>asynchronous actions of type {@link java.util.concurrent.Future} are not supported;</li>
* <li>the fallback, circuit breaker and retry strategies always inspect the cause chain of exceptions,
* following the behavior of SmallRye Fault Tolerance in the non-compatible mode.</li>
* </ul>
* @deprecated use {@link ApplyGuard}
*/
@Deprecated(forRemoval = true)
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
Expand All @@ -63,7 +23,8 @@
@Experimental("first attempt at providing reusable fault tolerance strategies")
public @interface ApplyFaultTolerance {
/**
* The identifier of a preconfigured fault tolerance instance.
* @deprecated use {@link ApplyGuard#value()}
*/
@Deprecated(forRemoval = true)
String value();
}
61 changes: 61 additions & 0 deletions api/src/main/java/io/smallrye/faulttolerance/api/ApplyGuard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.smallrye.faulttolerance.api;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import jakarta.interceptor.InterceptorBinding;

import io.smallrye.common.annotation.Experimental;

/**
* A special interceptor binding annotation to apply preconfigured fault tolerance.
* If {@code @ApplyGuard("<identifier>")} is present on a business method,
* then a bean of type {@link Guard} or {@link TypedGuard} with qualifier
* {@link io.smallrye.common.annotation.Identifier @Identifier("&lt;identifier>")}
* must exist. Such bean serves as a preconfigured set of fault tolerance strategies
* and is used to guard invocations of the annotated business method(s).
* <p>
* It is customary to create such bean by declaring a {@code static} producer field.
* That removes all scoping concerns, because only one instance ever exists. Using
* a non-static producer field or a producer method means that scoping must be carefully
* considered, especially if stateful fault tolerance strategies are configured.
* <p>
* The {@code @ApplyGuard} annotation may also be present on a bean class,
* in which case it applies to all business methods declared by the class. If the
* annotation is present both on the method and the class declaring the method,
* the one on the method takes precedence.
* <p>
* When {@code @ApplyGuard} applies to a business method, all other fault tolerance
* annotations that would otherwise also apply to that method are ignored.
* <p>
* A single preconfigured fault tolerance can be applied to multiple methods.
* If the preconfigured fault tolerance is of type {@code TypedGuard}, then all methods
* must have the same return type. If the preconfigured fault tolerance is of type {@code Guard},
* no such requirement applies; note that in this case, there is no way to define a fallback.
* <p>
* Note that this annotation has the same differences to standard MicroProfile Fault Tolerance
* as {@code Guard} / {@code TypedGuard}:
* <ul>
* <li>asynchronous actions of type {@link java.util.concurrent.Future} are not supported;</li>
* <li>the fallback, circuit breaker and retry strategies always inspect the cause chain of exceptions,
* following the behavior of SmallRye Fault Tolerance in the non-compatible mode.</li>
* </ul>
* If multiple beans of type {@code Guard} or {@code TypedGuard} with the same identifier
* exist, a deployment problem occurs.
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@InterceptorBinding
@Experimental("second attempt at providing reusable fault tolerance strategies")
public @interface ApplyGuard {
/**
* The identifier of a preconfigured {@link Guard} or {@link TypedGuard} instance.
*/
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,27 @@
import io.smallrye.common.annotation.Experimental;

/**
* Allows reading and observing current state of circuit breakers and reseting them to the initial (closed) state.
* Allows reading and observing current state of circuit breakers and resetting them to the initial (closed) state.
* To access a specific circuit breaker, it must be given a name using {@link CircuitBreakerName @CircuitBreakerName}
* or {@link FaultTolerance.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
* or {@link Guard.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
*/
@Experimental("first attempt at providing maintenance access to circuit breakers")
public interface CircuitBreakerMaintenance {
/**
* Returns a {@link CircuitBreakerMaintenance} instance that provides maintenance access to existing
* circuit breakers.
*/
static CircuitBreakerMaintenance get() {
return SpiAccess.get().circuitBreakerMaintenance();
}

/**
* Returns current state of the circuit breaker with given {@code name}.
* Note that there's no guarantee that the circuit breaker will stay in that state for any time,
* so this method is only useful for monitoring.
* <p>
* It is an error to use a {@code name} that wasn't registered using {@link CircuitBreakerName @CircuitBreakerName}
* or {@link FaultTolerance.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
* or {@link Guard.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
*/
CircuitBreakerState currentState(String name);

Expand All @@ -41,7 +49,7 @@ public interface CircuitBreakerMaintenance {
* changes state.
* <p>
* It is an error to use a {@code name} that wasn't registered using {@link CircuitBreakerName @CircuitBreakerName}
* or {@link FaultTolerance.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
* or {@link Guard.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
* <p>
* The callback must be fast and non-blocking and must not throw an exception.
*/
Expand All @@ -54,7 +62,7 @@ public interface CircuitBreakerMaintenance {
* and perhaps emergency maintenance tasks.
* <p>
* It is an error to use a {@code name} that wasn't registered using {@link CircuitBreakerName @CircuitBreakerName}
* or {@link FaultTolerance.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
* or {@link Guard.Builder.CircuitBreakerBuilder#name(String) withCircuitBreaker().name("...")}.
*/
void reset(String name);

Expand Down
Loading

0 comments on commit 2c72065

Please sign in to comment.