Enforcement path
One Lua eval per check
The gateway checks inline before proxying. Flip the Algorithm toggle to sliding-window counter and the Global exactness toggle to exact — then watch the metrics explain why production picks approx.
1 · Check inline at the edge
Gateway runs the check before proxying. Rules come from a local cache (30s TTL + version), so rule reads add no hop.
Test yourself
1. Why Lua?
2. Fixed-window flaw?
3. Redis down on a read path?
4. Why divide quota per DC?
Fixed vs log vs counter
Sliding windows without the O(n) bill
| Approach | Pros | Cons | Use when |
|---|---|---|---|
| Fixed window | 1 key, O(1) | 2× burst at :00 boundaries | Coarse quotas |
| Sliding log | Exact | O(n) memory (16GB+ here) | OTPs, password resets |
| Sliding counter | O(1), smooth | Tiny edge error | Standard throttles |
Counter formula: estimate = cur + prev × overlap. Two integer keys with
EXPIRE — no Lua needed. Lua stays for the token bucket, using Redis TIME
inside the script so client clocks can't over-admit.
Races, clocks, multi-DC
Fail open or closed? Per endpoint.
Read path fails open (serve, alert); money-mutations fail closed (503 + metric) — decided per rule, not globally. Multi-DC divides quota (each DC limit/3 + 20% headroom); exact global would cost a coordination RTT per request. First bottleneck at 10×: hot-slot on viral keys → gateway pre-aggregation + slot-splitting top-K keys.
Say it in the room
Interview one-liners
- Q: Why Lua? / A: Atomic refill-and-debit; without it concurrent checks over-admit.
- Q: Exact global limits? / A: No — divide per DC with headroom; exactness costs a RTT per request.
- Q: Limiter down? / A: Reads open, money-mutations closed; page either way.
Full write-up: Rate Limiter case note · Theory: Rate Limiting and Throttling, Linearizability · Next: Twitter Timeline playground →