2.8 KiB
2.8 KiB
backpressure.ts — bounded-queue accounting for the event-bus service
Purpose
Wraps @suaveplan/events' BoundedQueue with a small adapter that
treats the queue as a pure accounting primitive: a publish reserves a
slot, drains it immediately, and the adapter reports whether the
publish counts as "dropped" under the configured policy. Keeping this
logic in a focused module lets event-bus-service.ts stay under the
300-line file budget and makes the back-pressure semantics testable in
isolation.
Features
- Constructs a
BoundedQueue<unknown>plus a publiccapacity/policyview (the queue itself keeps these private). tryEnqueuetranslatesEVENTS.QUEUE.OVERFLOWintoEventBusError(EVENT_BUS.BACKPRESSURE_DROPPED).- Reports
dropped: truefordrop-oldest/drop-newestpolicies when the queue was at capacity before enqueue. - Forwards
AbortSignalthrough to the underlying queue.
Usage: Basic
import {
createBackpressureState,
tryEnqueue,
} from "@suaveplan/api-plugin-event-bus/services/event-bus-service";
const state = createBackpressureState({
capacity: 8,
policy: "drop-oldest",
});
const outcome = await tryEnqueue(state, { hello: "world" }, undefined);
console.log(outcome.dropped);
Usage: Advanced
import {
createBackpressureState,
tryEnqueue,
} from "@suaveplan/api-plugin-event-bus/services/event-bus-service";
const state = createBackpressureState({ capacity: 1, policy: "throw" });
await tryEnqueue(state, "a", undefined);
try {
await tryEnqueue(state, "b", undefined);
} catch (err) {
// EventBusError(EVENT_BUS.BACKPRESSURE_DROPPED)
}
API Reference
createBackpressureState(opts: BackpressureOptions): BackpressureState
Build the queue + companion accounting struct used by the service.
tryEnqueue(state, payload, signal): Promise<EnqueueOutcome>
Reserves a slot, drains it, and returns { dropped }. Throws
EventBusError(EVENT_BUS.BACKPRESSURE_DROPPED) under policy: "throw"
when the queue is full.
interface BackpressureState
{ queue: BoundedQueue<unknown>; capacity: number; policy: QueuePolicy }.
interface EnqueueOutcome
{ dropped: boolean }.
Implementation Notes
- We drain the queue immediately because we use it only for
accounting — the actual dispatch is owned by
EventBus.emitAsync. If the bus ever grows a queue-driven dispatch model, thedequeuecall disappears and the queue starts carrying the payload. - "would-overflow" is computed before
enqueuebecause underdrop-oldestthe queue swallows the oldest item synchronously and the post-enqueue size would not reveal the drop. - The thrown
EventBusErrorcarriescontext.statusCode: 503so the HTTP layer translates back-pressure rejections to "Service Unavailable" automatically.