How do you implement rate limiting and what algorithms exist?
Rate limiting is a traffic control mechanism that restricts how many requests a client can make within a time window. It protects services from abuse, prevents resource exhaustion, and ensures fair usage across tenants.
The four main algorithms each make different trade-offs. **Fixed window** divides time into discrete buckets and counts requests per bucket. It's simple to implement but suffers from the boundary burst problem: a client can fire 2× the limit by straddling two windows (e.g., 100 requests at 00:59 and 100 more at 01:01).
**Sliding window log** keeps a timestamp log of every request and counts entries within the rolling window. It's precise but memory-intensive—O(requests) space per client. **Sliding window counter** approximates the sliding window by combining the current and previous fixed-window counts weighted by overlap. It's O(1) space with ~0.1% error, making it the practical default.
**Token bucket** gives each client a bucket that refills at a constant rate (e.g., 10 tokens/second, capacity 50). Each request consumes one token; excess requests are dropped or queued. It allows controlled bursting up to bucket capacity—good for APIs where brief spikes are legitimate.
**Leaky bucket** processes requests at a constant outflow rate regardless of arrival rate, smoothing traffic absolutely. It cannot burst, which makes it ideal for protecting downstream systems that can't handle spikes.
In distributed systems, rate limiting state must be shared. A single Redis instance with atomic INCR/EXPIRE works for small scale. For higher scale, use Redis Cluster or a dedicated rate-limit service. The key design decisions: where to store counters (Redis, Memcached, or in-process with gossip-based sync), what the key is (IP, user ID, API key, tenant), and how to handle Redis unavailability (fail open vs. fail closed).
Sliding window counter in Redis is implemented with two keys per window and an atomic Lua script, ensuring no race conditions. Response headers like `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `Retry-After` are standard UX. HTTP 429 Too Many Requests is the correct status code.
| Aspect | Token Bucket | Leaky Bucket | Fixed Window | Sliding Window Counter |
|---|---|---|---|---|
| Burst allowed | Yes (up to capacity) | No | Yes (boundary burst) | Near-none |
| Memory per client | O(1) | O(1) | O(1) | O(1) |
| Precision | Exact | Exact | Approximate | High (~0.1% error) |
| Implementation complexity | Low | Low | Very low | Medium |
| Best use case | APIs with bursty clients | Protecting fragile downstreams | Simple quotas | General-purpose rate limiting |
Names at least two algorithms with correct descriptions, explains fixed-window boundary burst problem or token bucket burst semantics, mentions distributed state coordination.
Correctly contrasts all four algorithms with their space/time trade-offs, explains distributed coordination (Redis, Lua scripts for atomicity), discusses fail-open/fail-closed behavior, and mentions correct HTTP semantics (429, Retry-After).
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment