# `limits/outstanding-cap` ## Purpose Admission control for in-flight 2PC transactions. The coordinator holds a fixed-size budget of "outstanding prepared transactions"; once the budget is exhausted new `run2pc` calls refuse rather than queue. The default is 256 (design doc §14's open question, pinned by this package's spec) and operators can tune it via host config. Refusing rather than queuing is intentional: Postgres holds row-level locks for every prepared transaction, so a backed-up coordinator would otherwise convert a transient overload into a global lock storm. Failing fast lets callers route to a degraded path (degraded-write, retry queue, etc.). ## Features - `OutstandingPreparedCap` — synchronous counter with `admit()`, `release()`, `snapshot()`, `capacity()`. - Constructor validates that the cap is a positive integer (rejects zero, negatives, fractions, `NaN`). - `release()` is idempotent below zero — a buggy double-release in caller code cannot silently expand the real cap. - Synchronous-only — relies on the event-loop atomicity guarantee between `admit` / `release` pairs in JavaScript runtimes. ## Usage: Basic ```ts import { OutstandingPreparedCap } from "@suaveplan/api-2pc/limits"; const cap = new OutstandingPreparedCap(256); cap.admit(); try { // run the 2PC transaction } finally { cap.release(); } ``` ## Usage: Advanced Wrap admission inside the coordinator's public entry to centralise the release in one `finally`. The snapshot fuels the OTel gauge: ```ts import { TWOPC_CODES, TwoPcError } from "@suaveplan/api-2pc/error-codes"; import { OutstandingPreparedCap } from "@suaveplan/api-2pc/limits"; class MiniCoordinator { private readonly cap = new OutstandingPreparedCap(256); snapshot() { return this.cap.snapshot(); } async run(fn: () => Promise): Promise { try { this.cap.admit(); } catch (err) { if ( err instanceof TwoPcError && err.twoPcCode === TWOPC_CODES.OUTSTANDING_LIMIT_EXCEEDED ) { // route to degraded path throw err; } throw err; } try { return await fn(); } finally { this.cap.release(); } } } ``` ## API Reference - `class OutstandingPreparedCap` - `constructor(cap: number)` — throws `TWOPC_OUTSTANDING_LIMIT_EXCEEDED` on invalid `cap`. - `admit(): void` — throws `TWOPC_OUTSTANDING_LIMIT_EXCEEDED` when at capacity. - `release(): void` — idempotent below zero. - `snapshot(): number` — current outstanding count. - `capacity(): number` — configured cap. ## Implementation Notes Construction-time validation re-uses the same error code as the runtime cap-exceeded path. This is deliberate: operators tuning the cap via host config want one error string to alert on, regardless of whether the misconfiguration was a fractional value at startup or a real overload at steady state. The context object on the thrown `TwoPcError` distinguishes the two cases (`cap` only vs. `cap` + `outstanding`). The class is intentionally tiny — no async, no internal queue, no external dependencies. Adding a queue here would re-introduce the lock-storm risk the cap exists to prevent.