Initial Commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# `rate-limiter.ts`
|
||||
|
||||
## Purpose
|
||||
|
||||
`AdminRateLimiter` enforces the design §8.4 per-principal rate limits
|
||||
on every admin endpoint. Read endpoints share a 60-calls-per-minute
|
||||
bucket; destructive endpoints share a 1-per-minute bucket. Both buckets
|
||||
are scoped per principal id so a misbehaving operator cannot starve
|
||||
the host of admin throughput for everyone else.
|
||||
|
||||
The limiter is a thin wrapper around `@suaveplan/rate-limiter`'s
|
||||
`TokenBucket` — one bucket per `(principalId, kind)` pair, allocated
|
||||
lazily on first contact. The refill rate is set so the bucket starts
|
||||
full and drains to zero after `capacity` consecutive calls in a
|
||||
minute; refill is linear, so a steady stream is permitted at the
|
||||
documented rate. Bursts up to `capacity` are allowed.
|
||||
|
||||
## Features
|
||||
|
||||
- Per-principal isolation — one bucket pair per id.
|
||||
- Lazy allocation — principals that never hit admin are zero-cost.
|
||||
- Token-bucket refill maps `capacity` calls to one minute.
|
||||
- Injectable clock for deterministic tests.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { AdminRateLimiter } from "@suaveplan/api-host/admin-api";
|
||||
|
||||
const limiter = new AdminRateLimiter(60, 1);
|
||||
limiter.consume("alice", "read"); // true
|
||||
limiter.consume("alice", "destructive"); // true
|
||||
limiter.consume("alice", "destructive"); // false (until refill)
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
let now = 0;
|
||||
const limiter = new AdminRateLimiter(60, 1, () => now);
|
||||
limiter.consume("alice", "destructive"); // true
|
||||
limiter.consume("alice", "destructive"); // false
|
||||
now = 60_001;
|
||||
limiter.consume("alice", "destructive"); // true — refilled
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new AdminRateLimiter(readPerMinute, destructivePerMinute, clock?)`
|
||||
- `consume(principalId, kind): boolean` — `true` on success, `false`
|
||||
when rate-limited.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`TokenBucket`'s `consume(1)` updates the bucket's internal counter and
|
||||
returns a `RateLimitResult`. The admin API only cares about the
|
||||
`allowed` boolean — it surfaces `429` + `Retry-After` purely from
|
||||
constants when `consume` returns `false`.
|
||||
|
||||
The principal-id key is the rate-limit cardinality bound. A
|
||||
deployment with many distinct admin principals carries a small
|
||||
memory cost (~80 bytes per bucket pair) — gateways should rate-limit
|
||||
admin traffic at the edge before it reaches the host if extreme
|
||||
multi-tenant cardinality is anticipated.
|
||||
Reference in New Issue
Block a user