first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# `backpressure`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Bounds memory growth on a busy RPC channel by exposing two primitives:
|
||||
the logical `CallBudget` for outstanding in-flight calls, and the
|
||||
physical `BoundedQueue` for frames-in-transit and stream-chunk buffers.
|
||||
Both reject overflow rather than allocating without bound — the failure
|
||||
mode is a synchronous `WORKER_RPC.BACKPRESSURE` throw at the call site,
|
||||
not an OOM kill on the worker thread.
|
||||
|
||||
The two layers are deliberately distinct: a call budget counts logical
|
||||
requests (decremented on response receipt regardless of frame count),
|
||||
while the bounded queue counts wire frames (decremented on dequeue).
|
||||
Decoupling them lets streaming workloads — where one call yields many
|
||||
frames — back-pressure on chunk delivery without blocking new calls.
|
||||
|
||||
## Features
|
||||
|
||||
- `CallBudget(max)` with `acquire()` / `release()` semantics and a
|
||||
synchronous-throw exhaustion path.
|
||||
- `BoundedQueue(policy)` with three modes:
|
||||
- `"fail"` — throw `WORKER_RPC.BACKPRESSURE` on overflow.
|
||||
- `"drop-oldest"` — evict head, push new (head-drop).
|
||||
- `"drop-newest"` — refuse new, leaving the queue untouched.
|
||||
- `DEFAULT_BACKPRESSURE` — high-water-mark 64, mode `"fail"`.
|
||||
- Validation: both classes reject non-positive caps with
|
||||
`WORKER_RPC.BACKPRESSURE`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { CallBudget, DEFAULT_BACKPRESSURE, BoundedQueue } from "@suaveplan/api-worker-rpc/backpressure";
|
||||
|
||||
const budget = new CallBudget(1024);
|
||||
budget.acquire(); // throws WORKER_RPC.BACKPRESSURE when full
|
||||
budget.release();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { BoundedQueue, type BackpressurePolicy } from "@suaveplan/api-worker-rpc/backpressure";
|
||||
|
||||
const lossy: BackpressurePolicy = { highWaterMark: 8, mode: "drop-oldest" };
|
||||
const queue = new BoundedQueue<Frame>(lossy);
|
||||
queue.enqueue(frame); // never throws; head evicted on overflow
|
||||
|
||||
const strict: BackpressurePolicy = { highWaterMark: 1024, mode: "fail" };
|
||||
const strictQueue = new BoundedQueue<Frame>(strict);
|
||||
try {
|
||||
strictQueue.enqueue(frame);
|
||||
} catch (e) {
|
||||
if (e instanceof WorkerRpcError && e.code === "WORKER_RPC.BACKPRESSURE") {
|
||||
// back off
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface BackpressurePolicy { highWaterMark: number; mode: BackpressureMode }`.
|
||||
- `type BackpressureMode = "fail" | "drop-oldest" | "drop-newest"`.
|
||||
- `DEFAULT_BACKPRESSURE: BackpressurePolicy`.
|
||||
- `class CallBudget(max)` — `acquire()`, `release()`, `inFlight`, `capacity`.
|
||||
- `class BoundedQueue<T>(policy?)` — `enqueue(v): boolean`,
|
||||
`dequeue(): T|undefined`, `peek()`, `drain()`, `size`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `release()` is tolerant of extra calls (never goes negative) so
|
||||
late-arriving responses or duplicate acks cannot underflow the
|
||||
budget.
|
||||
- `drop-oldest` mutates in-place rather than allocating a new array
|
||||
so the buffer never grows beyond `highWaterMark`.
|
||||
- `drop-newest` returns `false` from `enqueue` so callers can
|
||||
distinguish accepted from refused; `drop-oldest` returns `true`
|
||||
unconditionally (the eviction is implicit).
|
||||
Reference in New Issue
Block a user