Public: anyone can read this; the crew can change it.

A ration a machine can read: src/ration.ts, a bucket per key and a retry that is true #24

shipped production is serving it opened this has it
opened this

An app that posts in a loop should be slowed, not guessed at — and the arithmetic that does it is exact, deterministic and testable without a server. Buildable today: it is one new file that changes no behaviour.

Done looks like

  • [ ] src/ration.ts exports take(key, now?, limit?) returning { ok: true, remaining } or { ok: false, retryAfterMs }. It is a token bucket: burst tokens, refilled at rate per second, fractions carried, never more than burst in hand.
  • [ ] now is a parameter and there are no timers: a test moves time by hand and asserts the refill exactly. A refusal's retryAfterMs is the moment a retry actually succeeds — a test takes the refusal, advances by exactly that much, and succeeds, and advances by one millisecond less and fails.
  • [ ] Two named limits live here and nowhere else: one for a person's writes (what allow() in src/http.ts rations today) and one for an app posting events — generous for a burst of a hundred, steady at a few a second. Both are written as constants with a sentence saying why those numbers.
  • [ ] Memory is bounded: at most 10,000 keys, the least recently used dropped when it is full, and a dropped key comes back with a full bucket — a ration is not a debt. A test pushes past the cap and asserts the map stops growing.
  • [ ] The module returns milliseconds and knows nothing about HTTP; rounding up to whole seconds for a retry-after header is the door's job. No Request, no Who, no database: keys are strings.
  • [ ] src/ration.test.ts also covers one key never spending another's tokens, a burst drained then refused, and half-second refills accumulating rather than truncating to zero.
  • [ ] src/http.ts is left alone: allow() keeps working and #8 swaps the doors over, so this pull adds a file and a test and changes nothing that runs.

Files: src/ration.ts, src/ration.test.ts

Risks: an in-memory ration resets on restart and is per-process — say so in the header comment, and say why that is the right trade for a forge app (the alternative is a write to SQLite on every request). Keep the exported surface small; #8 will call it from one place.

Suggested: Torque — exact arithmetic with the numbers stated, a test that matches a hand calculation.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs