extensions/smallrye-fault-tolerance/deployment/src/main/resources/META-INF/quarkus-skill.md
Apply MicroProfile Fault Tolerance annotations to CDI bean methods. Core annotations are in org.eclipse.microprofile.faulttolerance.*. SmallRye extensions (like @RateLimit) are in io.smallrye.faulttolerance.api.*.
@Retry(maxRetries = 3, delay = 500)
public String callService(String id) { ... }
// Selective retry
@Retry(maxRetries = 3,
retryOn = {ConnectException.class, TimeoutException.class},
abortOn = {InvalidInputException.class})
public String callService(String input) { ... }
retryOn — only retry on these exceptions (and subclasses)abortOn — stop retrying immediately on these exceptions (fallback still applies)@Timeout(2000) // 2 seconds (default unit is milliseconds)
public String fetchData() { ... }
Use unit = ChronoUnit.SECONDS for clarity: @Timeout(value = 2, unit = ChronoUnit.SECONDS).
@CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 5000)
public String callService() { ... }
Opens the circuit after failureRatio failures in requestVolumeThreshold calls. While open, calls fail immediately with CircuitBreakerOpenException. After delay ms, the circuit half-opens to probe.
@Retry(maxRetries = 3)
@Fallback(fallbackMethod = "fallbackGetData")
public String getData(String id) { ... }
private String fallbackGetData(String id) {
return "cached-" + id;
}
Fallback method must have the same parameters and return type. It can be private.
@Bulkhead(5)
public String limitedCall() { ... }
Excess calls throw BulkheadException. For async methods, also supports a waiting queue: @Bulkhead(value = 5, waitingTaskQueue = 10).
import io.smallrye.faulttolerance.api.RateLimit;
@RateLimit(value = 50, window = 1, windowUnit = ChronoUnit.MINUTES)
public String rateLimited() { ... }
Limits to 50 calls per minute. Excess calls throw RateLimitException.
Multiple annotations form a call chain (outermost to innermost):
Fallback → Retry → CircuitBreaker → RateLimit → Timeout → Bulkhead → method call
Retry re-attempts the entire inner chain. Fallback catches any exception that survives all retries. The order annotations appear in source doesn't matter — the chain order is fixed.
Methods returning CompletionStage or Uni automatically get async fault tolerance — no @Asynchronous annotation needed (Quarkus enables non-compatible mode by default):
@Retry(maxRetries = 3)
@Fallback(fallbackMethod = "asyncFallback")
public Uni<String> asyncCall(String id) { ... }
Override annotation values via application.properties:
quarkus.fault-tolerance."com.example.MyService/myMethod".retry.max-retries=5
quarkus.fault-tolerance."com.example.MyService/myMethod".circuit-breaker.request-volume-threshold=5
quarkus.fault-tolerance."com.example.MyService/myMethod".timeout.value=3000
@QuarkusTest
class FaultToleranceTest {
@Inject MyService service;
@Test
void testFallbackOnFailure() {
String result = service.getData("bad-id");
assertEquals("cached-bad-id", result);
}
}
For circuit breaker tests, use small requestVolumeThreshold and delay values. Circuit breaker state persists across test methods — use separate test classes or account for shared state.
@Timeout(2000) = 2 seconds. Use unit = ChronoUnit.SECONDS for clarity.new bypasses fault tolerance entirely.abortOn doesn't prevent fallback. It only skips retries — the Fallback layer (outermost) still catches the exception.requestVolumeThreshold = 20, failureRatio = 0.5, delay = 5000ms. Use smaller values in tests.@RateLimit is a SmallRye extension — import from io.smallrye.faulttolerance.api, not the MicroProfile package.