Options: fixed window (INCR with EXPIRE, simple, but allows double the requests at the window boundary), sliding window with a sorted set (accurate, uses more memory), token bucket (allows bursts, the most common in APIs).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # ====================================================================== # Q: Design a rate limiter: 100 requests per minute per API key. # # Options: fixed window (INCR with EXPIRE, simple, but allows double the # requests at the window boundary), sliding window with a sorted set (accurate, # uses more memory), token bucket (allows bursts, the most common in APIs). # Note: here we also count the requests that were rejected. For absolute # accuracy, write it as a Lua script. Return 429 with Retry-After and # X-RateLimit-Remaining headers. Redis is shared by all workers. # ====================================================================== def allow(api_key, limit=100, window=60): now = time.time() key = f'rl:{api_key}' pipe = r.pipeline() pipe.zremrangebyscore(key, 0, now - window) # drop the old entries pipe.zadd(key, {f'{now}:{uuid.uuid4().hex}': now}) pipe.zcard(key) pipe.expire(key, window) _, _, count, _ = pipe.execute() return count <= limit |