Rate limiter — admission control with a quota the memory enforces
On September 20, 2015, DynamoDB's storage servers all renewed their metadata leases at once after a network blip. The metadata service had no effective admission control; the retry storm starved it, which caused more timeouts, which caused more retries. The brownout took down large parts of us-east-1 for five hours and dragged twenty other AWS services with it. Cascading retry storms are what rate limiters exist to stop — IF the limiter itself cannot be talked out of its quota. This limiter's token bucket lives behind a memory invariant: tokens can never go below zero (no overdraft "just this once") and never above the burst ceiling (no refill bug minting infinite capacity). The breaker is a state machine with a one-way path through `shedding` and `recovering` — there is no edge from shedding straight back to closed-and-full-traffic, so the thundering herd cannot re-arrive at once.
A Soma program from the devops part of the verified corpus: 119 lines, cells RateLimiter, LimiterTests. It passes soma check with soma 3.2.4.
How it is built
The cell RateLimiter declares 6 signals in its face: admit(svc: String) -> Map, refill(svc: String, n: Int) -> Map, trip_breaker(svc: String) -> Map, start_recovery(svc: String) -> Map, breaker_close(svc: String) -> Map and tokens_left(svc: String) -> Int; keeps 1 memory slot: tokens (Map<String, Int>, persistent and consistent) under the invariant tokens >= 0 && tokens <= 10. Its state machine breaker starts in serving and allows 4 transitions: serving -> shedding, shedding -> recovering, recovering -> serving and recovering -> shedding. Handlers: admit (line 48), refill (line 60), trip_breaker (line 69), start_recovery (line 75), breaker_close (line 81) and tokens_left (line 87).
The file also holds 1 test cell (LimiterTests) with 16 assertions, run by soma test.
The source
rate_limiter.cell
// ============================================================================
// RATE LIMITER — admission control with a quota the memory enforces
// ============================================================================
//
// On September 20, 2015, DynamoDB's storage servers all renewed their
// metadata leases at once after a network blip. The metadata service had
// no effective admission control; the retry storm starved it, which
// caused more timeouts, which caused more retries. The brownout took
// down large parts of us-east-1 for five hours and dragged twenty other
// AWS services with it. Cascading retry storms are what rate limiters
// exist to stop — IF the limiter itself cannot be talked out of its quota.
//
// This limiter's token bucket lives behind a memory invariant: tokens
// can never go below zero (no overdraft "just this once") and never
// above the burst ceiling (no refill bug minting infinite capacity).
// The breaker is a state machine with a one-way path through `shedding`
// and `recovering` — there is no edge from shedding straight back to
// closed-and-full-traffic, so the thundering herd cannot re-arrive at once.
// ============================================================================
cell RateLimiter {
face {
signal admit(svc: String) -> Map
signal refill(svc: String, n: Int) -> Map
signal trip_breaker(svc: String) -> Map
signal start_recovery(svc: String) -> Map
signal breaker_close(svc: String) -> Map
signal tokens_left(svc: String) -> Int
}
memory {
// burst ceiling 10, floor 0. The overdraft and the infinite-
// refill bug are both rejected at the write.
tokens: Map<String, Int> [persistent, consistent]
invariant tokens >= 0 && tokens <= 10
}
// No shedding -> serving edge: recovery is mandatory, traffic
// returns gradually or not at all.
state breaker {
initial: serving
serving -> shedding
shedding -> recovering
recovering -> serving
recovering -> shedding
}
on admit(svc: String) {
if get_status(svc) == "shedding" {
return map("ok", false, "reason", "load shedding — request dropped at the door")
}
let t = tokens.get(svc) ?? 0
let w = try { tokens.set(svc, t - 1) }
if w.error != () {
return map("ok", false, "reason", "quota exhausted ({t} tokens)")
}
return map("ok", true, "tokens_left", t - 1)
}
on refill(svc: String, n: Int) {
let t = tokens.get(svc) ?? 0
let w = try { tokens.set(svc, t + n) }
if w.error != () {
return map("ok", false, "reason", "refill past burst ceiling refused")
}
return map("ok", true, "tokens_left", t + n)
}
on trip_breaker(svc: String) {
let r = try { transition(svc, "shedding") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "shedding")
}
on start_recovery(svc: String) {
let r = try { transition(svc, "recovering") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "recovering")
}
on breaker_close(svc: String) {
let r = try { transition(svc, "serving") }
if r.error != () { return map("ok", false, "reason", "{r.error}") }
return map("ok", true, "state", "serving")
}
on tokens_left(svc: String) {
return tokens.get(svc) ?? 0
}
}
cell test LimiterTests {
rules {
// a full bucket admits, then runs dry — and the dry bucket
// cannot be overdrawn: the -1 write is refused by memory
assert refill("meta", 3).ok == true
assert admit("meta").ok == true
assert admit("meta").ok == true
assert admit("meta").ok == true
assert admit("meta").ok == false
assert tokens_left("meta") == 0
assert_fails tokens.set("meta", -1)
// the infinite-refill bug: minting past the burst ceiling
assert refill("meta", 10).ok == true
assert refill("meta", 5).ok == false
assert_fails tokens.set("meta", 999)
// THE DYNAMODB MOVE: storm hits, breaker trips, and there is
// no edge that snaps full traffic back on in one step
assert trip_breaker("meta").ok == true
assert admit("meta").ok == false
assert_fails transition("meta", "serving")
assert start_recovery("meta").ok == true
assert breaker_close("meta").ok == true
assert admit("meta").ok == true
}
}What the toolchain says
$ soma check rate_limiter.cell
✓ All checks passed.$ soma test rate_limiter.cell
test LimiterTests ...
✓ assert refill("meta", 3).ok == true
✓ assert admit("meta").ok == true
✓ assert admit("meta").ok == true
✓ assert admit("meta").ok == true
✓ assert admit("meta").ok == false
✓ assert tokens_left("meta") == 0
✓ assert_fails tokens.set("meta", -1) — raised memory invariant violated on 'tokens': tokens >= 0 && tokens <= 10 — rejected write of -1 (key "meta"); the slot is unchanged
✓ assert refill("meta", 10).ok == true
✓ assert refill("meta", 5).ok == false
✓ assert_fails tokens.set("meta", 999) — raised memory invariant violated on 'tokens': tokens >= 0 && tokens <= 10 — rejected write of 999 (key "meta"); the slot is unchanged
✓ assert trip_breaker("meta").ok == true
✓ assert admit("meta").ok == false
✓ assert_fails transition("meta", "serving") — raised invalid transition: shedding → serving. Current state: 'shedding'. Valid targets: [recovering]
✓ assert start_recovery("meta").ok == true
✓ assert breaker_close("meta").ok == true
✓ assert admit("meta").ok == true
16 tests: 16 passed, 0 failed$ soma verify rate_limiter.cell
Verifying rate_limiter.cell...
State machine 'breaker': 3 states, initial 'serving'
States: [recovering, serving, shedding]
✓ 3 states, 4 transitions
✓ all states reachable from 'serving'
✓ reactive machine: no terminal state, and every reachable state can return to 'serving'
✓ no deadlocks
✓ liveness: every reachable state can return to 'serving' (reactive machine)
✓ think-isolated: CTL safety properties hold regardless of LLM output (3 handlers, 3 literal transitions, 0 dynamic)
✓ termination: all 6 handlers structurally terminate
✓ refinement: handler `trip_breaker` ⟶ {shedding}
✓ refinement: handler `start_recovery` ⟶ {recovering}
✓ refinement: handler `breaker_close` ⟶ {serving}
· transition sources: `trip_breaker`, `start_recovery`, `breaker_close` call transition(id, target) without declaring the source — the runtime refuses a move the graph forbids, but the proof does not see where the handler starts; `transition(id, from, to)` names the edge (checked by `soma check`, refused at run time from any other state)
RateLimiter/invariants:
✓ invariant tokens <= 10 — writer 'admit' proven by induction (writes t - 1)
⚠ invariant tokens >= 0 && tokens <= 10 — runtime-checked (computed values): admit → tokens [tokens >= 0] because `t - 1` is only known to lie in [-1, 9] — narrow it: `require (t - 1) >= 0 else …`, refill → tokens because `n` is a parameter (narrow it: `require (t + n) >= 0 && (t + n) <= 10 else …`)
11 passed, 1 warnings, 0 failures
Temporal properties for 'breaker':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK