MicroProfile Fault Tolerance
Retries, circuit breaker, timeout, bulkhead, fallback — patterns and gotchas.
# CLAUDE.md — MicroProfile Fault Tolerance
## Annotations
- `@Retry` — retry the method on failure.
- `@Timeout` — fail fast if the method runs too long.
- `@CircuitBreaker` — open the circuit on cascading failure.
- `@Bulkhead` — limit concurrent invocations.
- `@Fallback` — alternative path on failure.
- `@Asynchronous` — run on a separate thread pool.
Apply on methods that call external systems (HTTP, DB, message broker). Don't put them on every method "just in case".
## Retry
```java
@Retry(maxRetries = 3, delay = 200, jitter = 100, retryOn = IOException.class)
ChargeResponse charge(ChargeRequest req) { ... }
```
- Always set `delay` and `jitter` — synchronized retries cause thundering herds.
- Specify `retryOn` for transient errors only. Don't retry on `IllegalArgumentException` or domain failures.
- `abortOn = NotFoundException.class` — give up immediately for non-retryable errors.
## Timeout
```java
@Timeout(value = 5, unit = ChronoUnit.SECONDS)
ChargeResponse charge(...) { ... }
```
- The annotation works by interrupting the thread. The method must respect interruption.
- Timeout must be less than downstream's read timeout, or the client gives up before the server responds.
## Circuit breaker
```java
@CircuitBreaker(
requestVolumeThreshold = 10, // window of last 10 requests
failureRatio = 0.5, // 50% failure → open
delay = 5000 // half-open after 5s
)
ChargeResponse charge(...) { ... }
```
- Three states: closed (normal), open (failing fast), half-open (testing recovery).
- Use for downstreams that occasionally fail hard. Pointless for fast, reliable calls.
- Combine with `@Fallback` for graceful degradation when the circuit is open.
## Bulkhead
```java
@Bulkhead(value = 10) // max 10 concurrent calls
ChargeResponse charge(...) { ... }
```
- Limits resource exhaustion when one slow downstream blocks all threads.
- Pair with `@Asynchronous` for queue-based bulkhead with `waitingTaskQueue`.
## Fallback
```java
@Retry(maxRetries = 2)
@Fallback(fallbackMethod = "cachedCharge")
ChargeResponse charge(ChargeRequest req) { ... }
ChargeResponse cachedCharge(ChargeRequest req) { return cache.lookup(req); }
```
- The fallback method must have the same signature.
- For class-based fallbacks, implement `FallbackHandler<T>`.
- Don't use fallback to hide bugs. It's for graceful degradation, not error suppression.
## Stacking
- Order: `@Bulkhead` → `@Timeout` → `@CircuitBreaker` → `@Retry` → `@Fallback`.
- Retry is outermost: each retry counts as a fresh circuit-breaker call.
- Fallback is innermost: only invoked after all other strategies fail.
## Don't
- Don't retry POST requests without idempotency keys. You'll create duplicates.
- Don't set `maxRetries=Integer.MAX_VALUE`. Bound it.
- Don't use `@Asynchronous` for short calls. The thread-pool overhead exceeds the benefit.
- Don't use one big `@CircuitBreaker` for many distinct downstreams. One per logical dependency.
Other MicroProfile templates
MicroProfile Core Rules
MicroProfile Config, Health, Metrics — opinionated cloud-native Java defaults.
MicroProfile REST Client
Type-safe REST clients with @RegisterRestClient, async, and exception mapping.
MicroProfile JWT Authentication
JWT bearer auth with @LoginConfig, claim injection, and role-based access.
MicroProfile OpenAPI
OpenAPI 3 documentation with annotations, schemas, and operation metadata.