Skip to content

feat: exact in-flight concurrency cap via a Durable Object (#4) - #26

Merged
lex00 merged 1 commit into
mainfrom
feat/concurrency-gate
Sep 1, 2026
Merged

feat: exact in-flight concurrency cap via a Durable Object (#4)#26
lex00 merged 1 commit into
mainfrom
feat/concurrency-gate

Conversation

@lex00

@lex00 lex00 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Acceptance, against the issue

  • A DO caps concurrent/queued audits; excess gets 429ConcurrencyGate (one instance via idFromName("global")) hands out slot tokens up to MAX_IN_FLIGHT (6) and sheds with 429 + retry-after: 10 past it; the handler rejects with reason-logged reject reason=concurrency.
  • Falls back gracefully if the DO is unavailable — the gate is advisory: missing binding, get/fetch throw, or a non-ok non-429 answer all proceed uncapped (the KV per-minute breaker still stands). Only an explicit at-capacity answer sheds.

Shape

  • src/gate.ts: pure, clock-injected ConcurrencySlots ledger (unit-testable without a DO runtime) + the thin ConcurrencyGate DO over POST /acquire / /release. In-memory only — a DO is single-threaded so the count is exact while it lives, and an eviction resets to all-free, the harmless direction. Leaked slots (a worker that died between acquire and release) self-heal after STALE_MS (120s, generously above the fetch layer's caps); release is idempotent.
  • Handler: acquire after the rate-limit gate, release in finally on every path.
  • wrangler.toml: binding + v1 migration with new_sqlite_classes (the only class kind new migrations may create on the free plan; no storage is used).
  • README security section documents the cap and its two knobs.

Verification

  • 8 unit tests (cap/release/idempotence/stale-reclaim + the DO's HTTP surface), tsc --noEmit, wrangler deploy --dry-run resolving env.GATE.
  • Live on workerd: wrangler dev --local in fixture mode — binding active, audit flows through acquire/release.

Closes #4

🤖 Generated with Claude Code

The KV breaker bounds rate (audits per minute); nothing bounded
concurrency, so a burst that fit the per-minute budgets could still pile
simultaneous tree-walks onto the shared git token. One ConcurrencyGate
DO (idFromName("global")) now counts in-flight audits exactly and
sheds with 429 past MAX_IN_FLIGHT (6).

Advisory by design, per the issue's fallback criterion: a DO error or
missing binding never blocks an audit — only an explicit at-capacity
answer sheds. The slot ledger is in-memory (a DO is single-threaded, so
it is exact while the object lives; eviction resets to all-free, the
harmless direction), leaked slots self-heal after STALE_MS, and release
is idempotent. SQLite-backed class in the migration — the only kind new
migrations may create on the free plan; it uses no storage.

Verified on workerd via wrangler dev --local in fixture mode: binding
resolves, audits flow through acquire/release.

Closes #4

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lex00
lex00 merged commit c0c0cfd into main Sep 1, 2026
4 checks passed
@lex00
lex00 deleted the feat/concurrency-gate branch September 1, 2026 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat (optional): Durable Object for exact in-flight concurrency limit

1 participant