first commit
This commit is contained in:
75
docs/api-stack/api/api-sse-bun/src/limits/tenant-cap.md
Normal file
75
docs/api-stack/api/api-sse-bun/src/limits/tenant-cap.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# `tenant-cap`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The `tenant-cap` module owns the per-tenant SSE connection counter the
|
||||
engine consults during handshake. A tenant that has already reached
|
||||
`maxConnectionsPerTenant` open connections receives `429 Too Many
|
||||
Requests` with `SSE_BUN.TENANT_CAP_EXCEEDED` — the engine never opens
|
||||
the long-lived stream, so the per-tenant ceiling is enforced exactly
|
||||
at the handshake boundary and never as a mid-stream race.
|
||||
|
||||
## Features
|
||||
|
||||
- O(1) `tryAcquire` / `release` keyed by `TenantId`.
|
||||
- `count(tenantId)` exposed so OTel gauges can sample without exposing
|
||||
the internal map.
|
||||
- `total` aggregate count for global capacity dashboards.
|
||||
- Tight error context: `tenantId`, `limit`, `current` attached to every
|
||||
thrown `SseBunError` so operators do not need to grep request logs.
|
||||
- Idempotent `release` — releasing an unknown tenant does nothing.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createTenantConnectionCap } from "@suaveplan/api-sse-bun";
|
||||
|
||||
const cap = createTenantConnectionCap(1000);
|
||||
cap.tryAcquire("acme" as never);
|
||||
console.log(cap.count("acme" as never)); // 1
|
||||
cap.release("acme" as never);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
createTenantConnectionCap,
|
||||
SSE_BUN_CODES,
|
||||
SseBunError,
|
||||
} from "@suaveplan/api-sse-bun";
|
||||
|
||||
const cap = createTenantConnectionCap(2);
|
||||
const tenant = "acme" as never;
|
||||
cap.tryAcquire(tenant);
|
||||
cap.tryAcquire(tenant);
|
||||
try {
|
||||
cap.tryAcquire(tenant);
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof SseBunError &&
|
||||
err.code === SSE_BUN_CODES.TENANT_CAP_EXCEEDED
|
||||
) {
|
||||
// return 429 ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface TenantConnectionCap` — `tryAcquire(tenantId)`,
|
||||
`release(tenantId)`, `count(tenantId)`, `total`.
|
||||
- `function createTenantConnectionCap(max): TenantConnectionCap`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The cap uses a `Map<TenantId, number>` keyed by the branded tenant id;
|
||||
the brand prevents an unsanitised header value from accidentally being
|
||||
treated as a tenant key. `release` decrements when the count is `> 1`
|
||||
and deletes the entry on the last release so the map's footprint
|
||||
matches the live connection set. `total` is a running counter rather
|
||||
than a derived sum so reading it from the OTel callback is O(1).
|
||||
Reference in New Issue
Block a user