All Jakarta EE templates

MicroProfile Fault Tolerance

Retries, circuit breaker, timeout, bulkhead, fallback — patterns and gotchas.

DevZone Tools1,240 copiesUpdated Mar 30, 2026MicroProfileJakarta EEJava
# 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 Jakarta EE templates