Bulkheads keep one slow dependency from taking the pool

The name comes from ship compartments: a breach floods one section rather than the hull. Applied to a request path, it means each dependency gets a share of the workers and cannot exceed it.

// at most 4 of 12 workers may be waiting on payments
final class Bulkhead
{
    public function run($key, $limit, callable $work)
    {
        $n = $this->redis->incr("inflight:{$key}");
        $this->redis->expire("inflight:{$key}", 60);

        try {
            if ($n > $limit) { throw new TooBusy(); }
            return $work();
        } finally {
            $this->redis->decr("inflight:{$key}");
        }
    }
}

The finally is load-bearing: a decrement missed on an exception leaks the counter until the expiry, and the bulkhead closes permanently. The expiry is the safety net for a worker killed outright. Choosing the limit is the hard part and there is no formula — start at a third of the pool for anything non-essential and watch how often it rejects.