Atomic Sliding-Window Rate Limit

Distributed rate limiting fails when checking capacity and recording admission are separate operations.

Security · Redis Lua

Atomic Sliding-Window Limit

Admission and accounting must be one atomic decision at the scarce-resource boundary.

local key, now, window, limit = KEYS[1], ARGV[1], ARGV[2], tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
if redis.call('ZCARD', key) >= limit then return 0 end
redis.call('ZADD', key, now, now .. '-' .. ARGV[4])
redis.call('PEXPIRE', key, window)
return 1

Invariant: Admission and accounting happen atomically for one principal and window.

Use when: A distributed API needs a per-principal rate limit without race conditions.

Why this boundary matters

Separate check and increment operations race under concurrency. Atomic execution ensures every admitted request consumes exactly one place in the window.

Failure policy

BoundaryAction
Below limitAdmit and account atomically
Limit reachedReturn 429 with a useful Retry-After
Redis unavailableApply an explicit fail-open or fail-closed policy per endpoint
Clock skew possibleUse one trusted time source
Key is inactiveExpire its state to bound memory

Trade-offs

Sliding windows are fairer than fixed windows but cost more memory and operations. A centralized limiter gives consistent decisions but becomes a dependency on the admission path.

Decision rule: Rate-limit on the identity and resource that represent the scarce capacity, with failure behavior chosen by abuse and availability risk.

Further reference

Browse all engineering snippets

>