Design a Distributed Rate Limiter
How Stripe, Cloudflare, and Amazon protect their infrastructure from DDoS, brute-force bots, and noisy neighbors. Master Token Bucket, Leaky Bucket, Sliding Window Counter, Redis Lua Script atomicity, and HTTP 429 headers.
๐๏ธ 1. The Core Intuition: The Nightclub Bouncer with Tokens
Imagine a popular nightclub with a strict capacity limit:
1. The bouncer holds a bucket that can hold a maximum of 10 VIP Tokens (Bucket Capacity $B = 10$).
2. A mechanical conveyor belt drops 2 new tokens into the bucket every second (Refill Rate R = 2/sec).
3. When a party of 8 people arrives at once, the bouncer takes 8 tokens and lets them all in immediately! (Burst Traffic Handled).
4. When the bucket reaches 0 tokens, any new arrival is told: "Bucket is empty, please wait 0.5s for the next token" (HTTP 429 Too Many Requests).
2. Cost Control: Prevent runaway third-party API spend (OpenAI GPT-4 tokens, Twilio SMS, Stripe charges).
3. Preventing Cascading Failure: Prevent noisy neighbors from hogging database connection pools.
โ๏ธ 2. Deep Dive into the 5 Rate Limiting Algorithms
Every system design interview requires comparing these 5 algorithms:
Tokens added at a constant rate $R$ up to capacity $B$. Each request consumes 1 token. Allows sudden bursts of traffic while maintaining a strict long-term average rate. Requires only 2 numbers in memory per user: tokens and last_refill_timestamp ($O(1)$ RAM).
Requests enter a fixed-capacity FIFO queue and leak out at a strictly constant, smooth rate. Perfect for smoothing out bursty eCommerce flash-sales into a steady flow for databases.
Divides time into fixed 1-minute windows (12:00, 12:01) with a counter.
Keeps timestamps in a Redis Sorted Set. Removes timestamps older than 1 minute and counts remaining. 100% mathematically accurate, but memory-heavy ($O(M)$ RAM) because it stores a timestamp for every single request.
Combines fixed windows with a weighted rolling average formula:Requests = (Current Window Count) + (Previous Window Count ร (1 - Time Elapsed Fraction))Provides 99.95% accuracy with $O(1)$ memory!
โก 3. Eliminating Distributed Race Conditions with Redis Lua Scripts
When 50 API servers connect to a centralized Redis cache, naive code has a severe race condition:
tokens = redis.get(user_id)
if tokens > 0:
redis.set(user_id, tokens - 1)
return ALLOWUnder high concurrency, 10 parallel threads all read tokens = 1 at the same time and all 10 succeed, exceeding the rate limit!-- Executed atomically in Redis single-thread!
local current = redis.call('get', KEYS[1])
if tonumber(current) > 0 then
redis.call('decr', KEYS[1])
return 1
end
return 0โก Zero Race Conditions: Redis executes the entire Lua script atomically without locks or context switches.๐ 4. Standard HTTP 429 Headers Protocol
When a rate limit is exceeded, return HTTP 429 Too Many Requests with standard RFC headers:
๐ป 5. Production Token Bucket Rate Limiter (Multi-Language)
Production Token Bucket rate limiter with automated time refill across 5 languages:
# Python In-Memory Token Bucket with Timestamp Refill import time class TokenBucket: def __init__(self, capacity: int, refill_rate: float): self.capacity = capacity self.tokens = capacity self.refill_rate = refill_rate self.last_refill = time.time() def allow_request(self, tokens_needed: int = 1) -> bool: now = time.time() elapsed = now - self.last_refill # Refill tokens lazily without background cron! self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate) self.last_refill = now if self.tokens >= tokens_needed: self.tokens -= tokens_needed return True return False
๐งช 6. Interactive Lab: Live Rate Limiter Simulator
Distributed Rate Limiting Simulator
๐ฏ 7. Test Your Understanding
Your API is configured for a limit of 1,000 requests/minute using a Fixed Window Counter. A bot sends 1,000 requests at 00:00:59 and another 1,000 requests at 00:01:01. What happens?
Under high load, 100 API gateway instances concurrently check a user's rate limit in Redis. Why is a Redis Lua Script strictly required over individual GET and DECR commands?