Implementarea unui Rate Limiter de Înaltă Frecvență cu Redis și Lua în Redis & In-Memory Data Store

Ghid practic pentru implementarea algoritmului Sliding Window Rate Limiter în Redis utilizând scripturi Lua atomice pentru protecția API-urilor împotriva abuzurilor.

Sintaxă comandă
EVAL "local c = redis.call('incr', KEYS[1]) ..." 1 "rate:ip"

Una dintre cele mai răspândite utilizări ale Redis în companiile mari este Rate Limiting (limitarea ratei de cereri HTTP per adresă IP, token de utilizator sau cheie API). Pentru a preveni condițiile de concurență (race conditions) în mediile distribuite cu zeci de mii de cereri concurente, algoritmul optim se implementează printr-un script Lua executat atomic direct în interiorul serverului Redis via comanda EVAL.

Algoritmul Fixed Window Rate Limiter atomic prin Script Lua
-- Script Lua pentru verificare și incrementare atomică
-- KEYS[1] = cheia de identificare a ratei (ex: 'ratelimit:192.168.1.50')
-- ARGV[1] = limita maximă admisă (ex: 100 cereri)
-- ARGV[2] = fereastra de timp în secunde (ex: 60 secunde)

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])

local current = redis.call('get', key)

if current and tonumber(current) >= limit then
    -- Limita a fost atinsă, respinge cererea (HTTP 429 Too Many Requests)
    return 0
else
    -- Incrementează contorul
    current = redis.call('incr', key)
    if tonumber(current) == 1 then
        -- Dacă este prima cerere din fereastră, activează expirarea automată
        redis.call('expire', key, window)
    end
    -- Autorizează cererea
    return 1
end
Apelarea scriptului din linia de comandă redis-cli
# Execuție EVAL: 1 cheie ('ratelimit:ip:10.0.0.1'), limită 5 cereri, fereastră 60s
EVAL "local c = redis.call('get', KEYS[1]) if c and tonumber(c) >= tonumber(ARGV[1]) then return 0 else c = redis.call('incr', KEYS[1]) if tonumber(c) == 1 then redis.call('expire', KEYS[1], ARGV[2]) end return 1 end" 1 "ratelimit:ip:10.0.0.1" 5 60

# Returnează 1 -> Cererea este permisă (răspunde cu datele solicitate)
# Returnează 0 -> Limita a fost depășită (răspunde cu HTTP 429)

De ce să folosești Scripturi Lua în Redis

  • Atomicitate absolută garantată: Scriptul Lua se execută fără a fi întrerupt de nicio altă comandă, eliminând riscul ca doi utilizatori să depășească limita în aceeași milisecundă.
  • Zero latență de rețea intermediară: Logica condițională (if / else) rulează direct pe serverul Redis, fără a trimite valori înainte și înapoi către aplicația backend.
  • Optimizare prin SHA1: Scripturile pot fi pre-încărcate prin SCRIPT LOAD și apelate ultra-rapid prin codul hash cu EVALSHA.