bulkhead
v0.2.1@backendkit-labs/bulkhead
Limit concurrency. Queue the overflow. Shed excess load cleanly.
Overview#
Borrowed from naval architecture: isolate compartments so one breach does not sink the ship. The bulkhead pattern limits the number of concurrent calls to a downstream service — preventing a slow dependency from exhausting your thread pool or connection pool and bringing down unrelated features.
Unlike simple concurrency limiters (like p-limit), the bulkhead adds a wait queue with optional timeout: calls beyond maxConcurrent enter a FIFO queue and are executed as slots free up. If a call waits longer than queueTimeoutMs, it is rejected with a BulkheadTimeoutError. When even the queue is full, excess calls are shed immediately with { type: 'bulkhead_full' } — a clean, predictable signal your callers can act on.
How It Works#
Each Bulkhead instance maintains a counter of in-flight executions. When execute(fn) is called:
- →If in-flight < maxConcurrent → execute immediately.
- →If in-flight ≥ maxConcurrent and queue < maxQueue → enter FIFO queue.
- →If queued > queueTimeoutMs → reject with BulkheadTimeoutError.
- →If queue is full → return fail({ type: 'bulkhead_full' }) without waiting.
The BulkheadService aggregates metrics across all registered instances and automatically emits warnings when utilization is high — no manual monitoring setup required.
Configuration#
| Prop | Type | Description |
|---|---|---|
| name | string | Identifier for logs and metrics. |
| maxConcurrent | number | Maximum simultaneous in-flight executions. |
| maxQueue | number | Maximum callers waiting when all slots are busy. 0 = reject immediately (no queue). |
| queueTimeoutMs | number | Maximum time a call can spend waiting in the queue. Exceeded calls fail with BulkheadTimeoutError. |
NestJS Integration#
The @WithBulkhead decorator wraps any service method. The BulkheadRegistry provides pre-tuned factory methods for the four most common patterns, eliminating trial-and-error threshold tuning.
import { BulkheadModule } from '@backendkit-labs/bulkhead/nestjs'; @Module({ imports: [BulkheadModule.forRoot()] }) export class AppModule {} @Injectable() export class ExternalApiService { @WithBulkhead({ name: 'external-api', maxConcurrent: 10, maxQueue: 50, queueTimeoutMs: 5_000 }) async fetch(payload: FetchPayload): Promise<Result<ApiResponse, BulkheadError>> { return this.http.post<ApiResponse>('/api/data', payload); } // Stack with circuit breaker for full resilience @WithCircuitBreaker({ name: 'external-api', failureThreshold: 30 }) @WithBulkhead({ name: 'external-api', maxConcurrent: 10 }) async criticalFetch(id: string): Promise<Result<ApiResponse, ResilienceError>> { return this.http.get<ApiResponse>(`/api/data/${id}`); } }
BulkheadRegistry — pre-tuned factories
| Prop | Type | Description |
|---|---|---|
| getForClient(name) | Bulkhead | 5 concurrent, queue 20, timeout 2 s — for per-client isolation. |
| getForService(name) | Bulkhead | 20 concurrent, queue 200, timeout 10 s — for internal microservices. |
| getForDatabase(name) | Bulkhead | 15 concurrent, queue 150, timeout 5 s — for database connection pools. |
| getForHttpExternal(name) | Bulkhead | 8 concurrent, queue 50, timeout 10 s — for third-party HTTP APIs. |
Examples#
From basic to production-grade — copy and adapt.
import { Bulkhead } from '@backendkit-labs/bulkhead'; const bh = new Bulkhead({ name: 'external-api', maxConcurrent: 10, maxQueue: 50, }); const result = await bh.execute(() => externalApi.fetch(payload)); if (!result.ok) { if (result.error.type === 'bulkhead_full') { throw new TooManyRequestsException('Downstream overloaded'); } } return result.value;
⚖️ vs. Alternatives#
Comparing against p-limit and bottleneck — the most common concurrency-limiting libraries in Node.js.
| Feature | @backendkit-labs/bulkhead | p-limit | bottleneck |
|---|---|---|---|
| Concurrency limit | ✅ | ✅ | ✅ |
| Wait queue | ✅ maxQueue + rejection | ✅ Unlimited | ✅ Advanced |
| Explicit queue rejection | ✅ fail(bulkhead_full) | ❌ Never rejects | ⚠️ via options |
| Result<T,E> integration | ✅ Native | ❌ Returns promise | ❌ Callbacks |
| NestJS decorator | ✅ @UseBulkhead | ❌ | ❌ |
| Named instances (registry) | ✅ | ❌ | ❌ |
| Rate limiting | ❌ | ❌ | ✅ |
| Zero runtime deps | ✅ | ✅ | ❌ |
| Weekly downloads | Growing | ~50M | ~1M |
✅ Supported · ❌ Not supported · ⚠️ Partial / workaround needed. Download counts are approximate weekly npm averages.