Bulkhead

For introduction, see How to Limit Concurrency.

Description

If a guarded method is called by too many callers concurrently, the excess attempts will fail with BulkheadException.

If the gaurded method is @Asynchronous or @AsynchronousNonBlocking, excess attempts are first put into a queue. The queued attempts are executed later. When the queue becomes full, excess attempts are rejected with BulkheadException.

Lifecycle

Bulkhead needs to maintain some state between invocations: the number of current executions and the queue of asynchronous executions. This state is a singleton, irrespective of the lifecycle of the bean that uses the @Bulkhead annotation.

More specifically, the bulkhead state is uniquely identified by the combination of the bean class (java.lang.Class) and the method object (java.lang.reflect.Method) representing the guarded method.

For example, if there’s a guarded method doWork on a bean which is @RequestScoped, each request will have its own instance of the bean, but all invocations of doWork will share the same bulkhead state.

Interactions with Other Strategies

See How to Use Multiple Strategies for an overview of how fault tolerance strategies are nested.

If @Fallback is used with @Bulkhead, the fallback method or handler may be invoked if a BulkheadException is thrown, depending on the fallback configuration.

If @Retry is used with @Bulkhead, each retry attempt is processed by the bulkhead as an independent invocation. If BulkheadException is thrown, the execution may be retried, depending on how retry is configured.

If @CircuitBreaker is used with @Bulkhead, the circuit breaker is checked before enforcing the concurrency limit. If concurrency limiting results in BulkheadException, this may be counted as a failure, depending on how the circuit breaker is configured.

If @RateLimit is used with @Bulkhead, the rate limit is enforced before enforcing the concurrency limit.

If @Timeout is used with @Bulkhead, the timeout watcher is started before enforcing the concurrency limit.

Configuration

There are 2 configuration options, corresponding to the 2 members of the @Bulkhead annotation.

value

Type: int

Default: 10

The concurrency limit.

waitingTaskQueue

Type: int

Default: 10

The size of queue for excess attempts. Only effective when the method is @Asynchronous or @AsynchronousNonBlocking. See the Asynchronous Execution reference guide.

Metrics

Bulkhead exposes the following metrics:

Name

ft.bulkhead.calls.total

Type

Counter

Unit

None

Description

The number of times the bulkhead logic was run. This is usually once per method call, but may be zero times if the circuit breaker or rate limit prevented execution or more than once if the method call was retried.

Tags

  • method - the fully qualified method name

  • bulkheadResult = [accepted|rejected] - whether the bulkhead allowed the method call to run

Name

ft.bulkhead.executionsRunning

Type

Gauge<Long>

Unit

None

Description

Number of currently running executions.

Tags

  • method - the fully qualified method name

Name

ft.bulkhead.executionsWaiting

Type

Gauge<Long>

Unit

None

Description

Number of executions currently waiting in the queue.

Tags

  • method - the fully qualified method name

Notes

Only added if the method is also @Asynchronous or @AsynchronousNonBlocking

Name

ft.bulkhead.runningDuration

Type

Histogram

Unit

Nanoseconds

Description

Histogram of the time that method executions spent running.

Tags

  • method - the fully qualified method name

Name

ft.bulkhead.waitingDuration

Type

Histogram

Unit

Nanoseconds

Description

Histogram of the time that method executions spent waiting in the queue.

Tags

  • method - the fully qualified method name

Notes

Only added if the method is also @Asynchronous or @AsynchronousNonBlocking

See the Metrics reference guide for general metrics information.