Redis Lua: Implement a Rate Limiter with Sliding Window
local function rate_limit(key_prefix, limit, window_seconds) -- Ensure inputs are of correct types and values. if type(key_prefix) ~= 'string' or #key_prefix == 0 then return redis.error_reply('Invalid key_prefix: must be a non-empty string.') end if type(limit) ~= 'number' or li...
This Redis Lua script implements a sliding window rate limiter. It uses a Redis Sorted Set to store the timestamps of incoming requests within a defined time window. When a new request arrives, the script first removes a...
The script defines a `rate_limit` function that takes a `key_prefix`, a `limit`, and `window_seconds` as arguments. It performs input validation to ensure these parameters are of the correct type and value. It then calculates the `current_time_ms` and `window_start_ms`. A Redis Sorted Set, keyed by `key_prefix .. ':timestamps'`, is used to store request timestamps. The `ZREMRANGEBYSCORE` command atomically removes all timestamps older than `window_start_ms`. `ZCARD` then efficiently retrieves the current number of requests within the window. If `current_requests` is greater than or equal to `limit`, the function returns 0, indicating the rate limit is exceeded. Otherwise, the current timestamp is added to the sorted set using `ZADD`, and the key is given an expiration slightly longer than the window using `EXPIRE` to ensure automatic cleanup. The function then returns 1, allowing the request. The time complexity for each request is dominated by the Redis commands: `TIME`, `ZREMRANGEBYSCORE`, `ZCARD`, `ZADD`, and `EXPIRE`. In the worst case, `ZREMRANGEBYSCORE` might iterate through many old elements, but on average, its cost is amortized. The overall time complexity per request is effectively O(log N) or O(1) depending on the Redis command implementation details and the number of elements removed, where N is the number of requests in the window. The space complexity is O(K), where K is the number of requests within the `window_seconds`. Edge cases handled include invalid input parameters and the scenario where the sorted set is empty (the first request in a window), which correctly results in the request being allowed.
function rate_limit(key_prefix, limit, window_seconds): validate key_prefix, limit, window_seconds current_time_ms = get current time in milliseconds window_start_ms = current_time_ms - (window_seconds * 1000) timestamp_...