Architecture patterns
Local plus global limits
How high-volume limiters avoid a Redis call per request by deciding locally and syncing every few hundred milliseconds, and what overshoot that costs, worked through a 1 million requests per second design.
A Redis round trip costs about 0.3 to 1 ms and puts Redis on your hot path. At very high volume, that is too much. The hybrid design lets each server decide locally and syncs the counts to a shared store on a timer. The price is a small, bounded overshoot.
The big idea
Each gateway keeps its own token bucket for each key. Its size is its share of the global limit: the global limit divided by the number of servers. Requests are decided in memory. Every 100 ms to 1 s, each server reports what it consumed and receives the updated global view.
Envoy works this way in practice. Its local rate limit filter needs no network call. Its optional global service, Lyft's open-source ratelimit backed by Redis, handles limits that must be shared.
The trade-off is accuracy. Suppose a client's traffic lands on only 10 of 200 servers. Each server's share is 1/200 of the limit, so the client is capped far below its real limit. If sync is slow, the client can overshoot. Most products accept a few percent of overshoot in exchange for removing a network hop.
Walking through it: a 1 million requests per second design
Requirements. An API gateway in three regions serves 1 million requests per second at peak. Limits are per API key, set by plan (for example, 100 requests per second with bursts of 500). There are about 10 million active keys. The limiter must add under 1 ms at p99 and must not take the API down if it fails.
State. A token bucket needs two numbers plus the key, about 100 bytes in Redis with overhead:
10,000,000 keys × 100 bytes ≈ 1 GB per regionMemory is not the problem.
Throughput. Traffic splits 50% / 30% / 20% by region, so the largest region sees about 500,000 checks per second. A Redis shard can handle roughly 50,000 scripted checks per second with headroom:
500,000 checks/s ÷ 50,000 per shard = 10 shards, plus a replica eachThat works, but it puts Redis on every request. The hybrid design cuts that sharply.
Design.
- Gateways decide locally with an in-memory bucket per active key. Most requests never leave the process.
- Regional sync. Every 250 ms, each node sends one batched "key to tokens consumed" call. With 200 nodes per region, that is 800 calls per second instead of 500,000 checks.
- Strict keys go straight to Redis. Login attempts and low-limit keys use the script on every request.
- Cross-region splits. A key's limit is divided by recent traffic. If a 100/s key gets 70% of its traffic in us-east, us-east enforces 70/s. A job rebalances about once a second.
Overshoot. During one 250 ms sync interval, nodes can each admit their share before learning the global count is used up. The bound is the rate times the interval: 100/s × 0.25 s = 25 requests. The sync interval is the knob.
Common mistakes
- Splitting the limit evenly when traffic is uneven. A client on 10 of 200 servers gets capped near 5/s, even though its limit is 100/s.
- Treating the hybrid as exact. It is bounded, not exact. Write the bound down.
- Coordinating across regions on every request. That adds 70 to 150 ms. Nobody does that for rate limits.
Check yourself
-
A 100/s key's traffic lands on 10 of 200 servers, each holding 1/200 of the limit. What goes wrong?
Answer: each server allows only 0.5 requests per second, so the client is capped near 5/s, far below its 100/s limit.
-
Why is the overshoot bounded?
Answer: nodes can run ahead only until the next sync, so the overshoot is about rate × interval, here 100 × 0.25 = 25 requests.
Key takeaways
- Redis on every request adds latency and makes Redis part of the hot path.
- Local buckets sized to each server's share remove that hop.
- Periodic sync bounds the error, and the sync interval is the tuning knob.
- Uneven traffic makes even splits unfair, so watch hot keys.
Further learning
Next, see what a client should see when it is refused, and how quotas differ from rate limits, in responses and quotas.
Related
Distributed limiter on Redis
How many servers share one rate-limit count without races, using an atomic Redis Lua script, one clock, expiring keys and a deliberate failure mode.
Rate limiting
Why systems cap how much work a client can send, which algorithm counts, where the limit lives and what the client is told. Start here for the rate-limiting map.
Load balancing and proxies
How a load balancer chooses which server gets each request, and how a badly set up balancer can cause the outage it was meant to prevent.
Multi-region architecture
Active-passive, active-active and cell-based designs for latency, disaster tolerance and data residency, with the recovery, consistency and cost trade-offs each one carries.