async-bulkhead-llm - v3.17.0
    Preparing search index...

    Stats and events

    const s = bulkhead.stats();

    s.limits.revision
    s.limits.maxConcurrent
    s.limits.maxQueue
    s.limits.tokenBudget?.budget
    s.limits.tokenBudget?.highPriorityReserve
    s.limits.admissionClasses?.premium?.maxConcurrent

    s.bulkhead.inFlight
    s.bulkhead.pending
    s.bulkhead.maxConcurrent
    s.bulkhead.maxQueue
    s.bulkhead.closed // true after close()

    s.llm.admitted
    s.llm.released
    s.llm.inFlight
    s.llm.borrowedConcurrencyAbandoned
    s.llm.borrowedConcurrencyAbandonedByCause?.deadline
    s.llm.rejected
    s.llm.rejectedByReason?.budget_limit

    s.observe?.bypassed // callbacks run without capacity
    s.observe?.raceBypassed // advisory admit raced with rejection
    s.observe?.bypassedByReason
    s.observe?.usageReported
    s.observe?.totalInputTokens
    s.observe?.totalOutputTokens

    s.tokenBudget?.budget
    s.tokenBudget?.inFlightTokens
    s.tokenBudget?.available
    s.tokenBudget?.totalReserved // cumulative tokens reserved at admission
    s.tokenBudget?.totalConsumed // cumulative actual tokens consumed (usage required)
    s.tokenBudget?.totalRefunded // cumulative refunded tokens

    s.admissionClasses?.defaultClass
    s.admissionClasses?.classes.premium?.inFlight
    s.admissionClasses?.classes.premium?.inFlightTokens
    s.admissionClasses?.classes.premium?.rejectedByReason

    s.deduplication?.active
    s.deduplication?.hits

    Stats are namespaced: underlying bulkhead slot lifecycle lives in stats().bulkhead, and higher-level LLM request outcomes such as budget_limit live in stats().llm.

    type LLMStats = {
    limits: LLMAdmissionLimits;
    bulkhead: Stats;
    llm: {
    inFlight: number;
    admitted: number;
    released: number;
    borrowedConcurrencyAbandoned: number;
    borrowedConcurrencyAbandonedByCause: Partial<
    Record<LLMBorrowedConcurrencyAbandonCause, number>
    >;
    rejected: number;
    rejectedByReason: Partial<Record<LLMRejectReason, number>>;
    };
    tokenBudget?: {
    budget: number;
    inFlightTokens: number;
    available: number;
    totalReserved: number;
    totalConsumed: number;
    totalRefunded: number;
    };
    observe?: {
    bypassed: number;
    raceBypassed: number;
    bypassedByReason: Partial<Record<
    'budget_limit' | 'concurrency_limit' | 'queue_limit' | 'timeout',
    number
    >>;
    usageReported: number;
    totalInputTokens: number;
    totalOutputTokens: number;
    };
    admissionClasses?: {
    defaultClass: string;
    shared: {
    maxConcurrent: number;
    inFlight: number;
    availableConcurrent: number;
    tokenBudget?: {
    budget: number;
    inFlightTokens: number;
    available: number;
    };
    };
    classes: Record<string, LLMAdmissionClassStats>;
    };
    deduplication?: {
    active: number;
    hits: number;
    };
    };
    // Subscribe
    const off = bulkhead.on('admit', ({ admissionId, reservedTokens }) => {
    console.log(`${admissionId}: ${reservedTokens} tokens reserved`);
    });

    // Unsubscribe
    off();
    Event Payload
    admit { request, admissionId, limitRevision, priority, admissionClass?, reservedTokens, resources, decisionDurationNs?, queueWaitNs? }
    borrowedConcurrencyAbandon { request, admissionId, limitRevision, priority, admissionClass?, resources, cause, heldTokens }
    reject { request, reason, limitRevision, admissionClass?, detail?, decisionDurationNs?, queueWaitNs? }
    usage { request, admissionId, limitRevision, priority, admissionClass?, sequence, reservedTokens, previousHeldTokens, heldTokens, deltaTokens, usage, outputCap, outputRemaining, overReservation }
    release { request, admissionId, limitRevision, priority, admissionClass?, reservedTokens, resources, heldTokens, refundedTokens, usageSequence, usage? }
    bypass { request, admissionId, limitRevision, priority, admissionClass?, reason, detail?, reservation, raced }
    bypassUsage { request, admissionId, limitRevision, priority, admissionClass?, reason, sequence, reservation, usage, outputCap, outputRemaining, overReservation }
    bypassRelease { request, admissionId, limitRevision, priority, admissionClass?, reason, reservation, usageSequence, usage? }
    reconfigure { previous, current }
    dedup { request }

    Listeners are synchronous and fire-and-forget. Exceptions are silently caught. Do not perform blocking network I/O in a listener; enqueue telemetry or lease updates for asynchronous delivery.

    decisionDurationNs and queueWaitNs are integer nanoseconds measured with Node's monotonic process.hrtime.bigint() clock. They are optional because some reject events represent deduplication-follower failures rather than a new admission decision, and observe-mode calls that become bypasses are not timed as admissions.

    The public timing boundary is:

    Path queueWaitNs decisionDurationNs
    precheck reject exactly 0 precheck span
    acquire reject / timeout full awaited span precheck + acquire-option normalization + reject-detail build
    postcheck reject full awaited span precheck + acquire-option normalization + postcheck + acquired-slot cleanup + detail build
    admit (unqueued or queued) full awaited span precheck + acquire-option normalization + postcheck + revision capture + uuid + wrapToken

    decisionDurationNs excludes the awaited bulkhead.acquire() in every case. Use it for local admission-decision cost. Use queueWaitNs separately for contention / intentional waiting; do not add queue wait to the decision number and call the sum admission overhead.

    The second observe-mode bypass path is a compatibility wrinkle: if an advisory decision is positive but _acquire() later loses a race with a shadowable limit, the existing reject event still fires before run() converts the result into bypass. That reject event deliberately omits both timing fields.

    const off = bulkhead.on('release', ({ reservedTokens, refundedTokens, usage }) => {
    metrics.histogram('llm.tokens.reserved', reservedTokens);
    metrics.histogram('llm.tokens.refunded', refundedTokens);
    });