Course contentsShow
← Databases & SQL
Lesson 1300 of 1,95312. Key-Value and Caching EnginesFree lesson

Distributed Rate Limiting

Apply token bucket algorithms across distributed instances using atomic counters.

In this lesson, you will learn how to implement a distributed token bucket algorithm to enforce rate limits across multiple application instances using atomic operations.

The Token Bucket Challenge

When you scale your application horizontally, a local variable tracking requests per second (like a simple counter in memory) becomes useless because each server instance has its own isolated memory. To ensure a user doesn't exceed a global limit (e.g., 100 requests per minute), you need a centralized source of truth. Redis is the industry standard for this because it allows us to store the bucket state and modify it atomically.

Applying Atomic Counters

The Token Bucket algorithm works by treating the limit as a "bucket" of tokens. Every request consumes a token. If the bucket is empty, the request is rejected.

Instead of building complex custom logic, we use a Lua script to execute a "check-and-set" operation inside Redis. This ensures that the read and write steps happen as a single atomic unit, preventing race conditions where two simultaneous requests from different servers both see one remaining token and consume it at the same time.

Here is a simplified logic flow:

-- Lua script executed atomically on Redis
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")

if current + 1 > limit then
    return 0 -- Reject request
else
    redis.call('incr', key)
    redis.call('expire', key, 60) -- Reset every 60 seconds
    return 1 -- Accept request
end

Why Atomicity Matters

Without atomicity, your distributed instances would suffer from "over-counting." If two servers query the current count simultaneously, they might both read 99, both calculate that they are under the 100 limit, and both increment the value to 101. By using atomic operations (like INCR or a Lua script), we guarantee that the sequence of "check if under limit" and "increment the counter" is thread-safe across the entire cluster.

By offloading this logic to the cache engine, you maintain a consistent global state that keeps your database from being overwhelmed by traffic spikes, regardless of which server node handles the incoming request.

Key Takeaway: To enforce global rate limits in distributed systems, use atomic increment operations or Lua scripts in a centralized cache to ensure the state is consistent across all application nodes.