Skip to content

Bulkhead

Arnel Robles edited this page Sep 29, 2026 · 1 revision

Bulkhead (Compartment)

Package: Carom.Extensions.

using Carom.Extensions;

var db = Compartment.ForResource("database")
    .WithMaxConcurrency(10)
    .WithQueueDepth(20)
    .Build();

var rows = await CaromCompartmentExtensions.ShotAsync(() => repo.QueryAsync(sql), db, ct: cancellationToken);

Defaults and rules

Setting Default Rule
WithMaxConcurrency 10 At least 1
WithQueueDepth 0 Not negative

What happens when it is full

A free slot is taken at once. If none is free and the queue has room, the caller takes a queue place and waits for a slot. If the queue is full too, the call fails at once with CompartmentFullException (it carries ResourceKey and MaxConcurrency) and CaromHooks.OnBulkheadRejected fires. There is no wait timeout; pass a cancellation token to bound the wait.

A caller that cancels while queued gets OperationCanceledException, not CompartmentFullException, and its queue place is freed. No rejection is signalled. (Fixed in 2.0.1.)

Retry and the bulkhead

CaromCompartmentExtensions runs the retry outside the bulkhead, so each attempt enters the compartment again. The default retry predicate skips CompartmentFullException: a full bulkhead is load shedding, and retrying it adds load.

State is shared by resource key, the same way as the circuit breaker.

Clone this wiki locally