Mock Distributed Rate Limiter with Dict in Python
Simulates a distributed token-bucket rate limiter with a thread-safe dict, useful for testing before moving to Redis.
Python code
48 linesimport time
import threading
from collections import defaultdict
class DistributedRateLimiter:
"""
A mock distributed rate limiter using a dict with thread-safe access.
Implements a token bucket algorithm per user.
"""
def __init__(self, rate_per_second=5, burst_capacity=10):
self.rate_per_second = rate_per_second
self.burst_capacity = burst_capacity
self._buckets = defaultdict(lambda: {"tokens": burst_capacity, "last_refill": time.time()})
self._lock = threading.Lock()
def allow_request(self, user_id):
with self._lock:
bucket = self._buckets[user_id]
now = time.time()
elapsed = now - bucket["last_refill"]
bucket["tokens"] = min(
self.burst_capacity,
bucket["tokens"] + elapsed * self.rate_per_second
)
bucket["last_refill"] = now
if bucket["tokens"] >= 1:
bucket["tokens"] -= 1
return True
return False
if __name__ == "__main__":
limiter = DistributedRateLimiter(rate_per_second=2, burst_capacity=3)
user = "user_42"
# Simulate burst of requests
for _ in range(5):
print(f"Request allowed: {limiter.allow_request(user)}")
# Wait for bucket to refill
time.sleep(1)
print(f"After 1s wait, request allowed: {limiter.allow_request(user)}")
print(f"Request allowed: {limiter.allow_request(user)}")
Output
Request allowed: True
Request allowed: True
Request allowed: True
Request allowed: False
Request allowed: False
After 1s wait, request allowed: True
Request allowed: True
How it works
The token bucket algorithm maintains a token count per user, refilling at a fixed rate. The defaultdict creates a fresh bucket per user on first access, avoiding manual key setup. A threading.Lock serializes mutations, preventing race conditions in multi-threaded simulations. Waiting 1 second refills approximately 2 tokens, enough for the next two requests. This mock mirrors Redis-based designs where the bucket state lives in a shared store, replacing the dict with Lua scripts or Redis commands in production.
Common mistakes
- Forgetting to use a lock and hitting race conditions in concurrent code
- Not resetting `last_refill` after updating tokens, causing over-refill
- Using float equality checks that can lead to precision errors
- Hardcoding burst capacity instead of deriving from the rate
Variations
- Use Redis with `INCR` and `EXPIRE` for a fixed-window counter
- Implement sliding window log with `ZSET` for more precise control
Real-world use cases
- Load-testing rate limiting logic locally without spinning up a Redis instance
- Prototyping quota enforcement for API keys in a single-process service
- Teaching token bucket concepts in interview prep or team onboarding
Sponsored
More from Reliability & rate limiting
- At Least Once with Idempotent Consumer in Python medium
- Build a Rate Limiter Decorator in Python easy
- Build a queue-based admission control system in Python easy
- Chaos Inject Random Failures in Python easy
- Circuit breaker failure threshold count in Python medium
- Exactly Once Processing Dedupe Mock in Python easy
Keep learning
Related tutorials and quizzes for this topic.