Rate limiter design: algorithm choice, shared storage, and atomic operations
A rate limiter controls how many requests a client can make in a time window. The first decision is the algorithm. Token bucket and leaky bucket allow bursts up to a bucket size and then smooth to a refill rate, which matches most API use cases. Fixed window counters are simple but have a boundary problem: a client can send a full burst at the end of one window and another at the start of the next, doubling the effective rate. Sliding window log stores timestamps and gives exact counts but uses memory proportional to the number of requests. Sliding window counter approximates by weighting the previous window and is a good middle ground. The second decision is storage. A single-process limiter can use an in-memory dict, but any multi-instance service needs a shared store, typically Redis, with atomic operations. Doing read-modify-write in Python is a race condition under concurrency; you need a Lua script or an atomic command like INCR with EXPIRE, or Redis sorted sets with ZADD and ZREMRANGEBYSCORE for sliding windows. The third decision is scope and failure mode: per user, per API key, per IP, or global. Decide whether to fail open or closed when Redis is unavailable, and make that explicit in the design.
Token bucket: O(1) per request, supports bursts, easy to reason about. Store tokens and last refill time per key.
Sliding window log: exact, no boundary problem, but memory grows with request rate. Use sorted sets in Redis.
Fixed window: simplest, but burst at boundaries can double the limit. Only acceptable for coarse limits.
Distributed: use Redis with atomic Lua scripts or built-in atomic ops. Never do get-then-set from Python.
Trade-off: exactness versus memory and latency. Token bucket is usually the best default for APIs.
Common mistake: implementing the limiter in the web process and assuming it works across pods. It does not.
Common mistake: using time.time() in multiple processes without a shared clock; use Redis server time or monotonic clocks where possible.
Version note: Redis 7+ supports functions; Lua scripts have been supported for a long time. Use whichever your infrastructure supports.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience