Sliding Window Log: Strict Limits Without Boundary Spikes
The failure mode it solves
Fixed windows reset at sharp boundaries. That allows double-spend bursts and makes limits feel unfair. Sliding Window Log fixes that by enforcing a moving time window: every request is evaluated against the last N milliseconds, not against a wall clock boundary.
This is the most accurate algorithm in the project, and also the most expensive in Redis. You use it when fairness and precision matter more than raw throughput.
How the algorithm works in this service
Each IP + endpoint key maps to a Redis sorted set of timestamps. On every request, the Lua script removes entries older than the window, counts remaining entries, and decides allow or reject. If allowed, it inserts the current timestamp.
Step-by-step flow:
- Compute
window_start = now - windowTime. - Remove all timestamps older than
window_start. - Count the remaining entries.
- If
count < maxRequests, addnowand allow. Otherwise reject. - Set TTL to
2 * windowTimeto cover late cleanup safely.
Diagram
The important detail is that the window slides per request. There is no shared boundary, so there is no boundary spike.
Worked example: strict rolling window
Window is 60 seconds with maxRequests 100. At t=0, a client sends 60 requests and they are all allowed. At t=30s, they send 40 more, still allowed. At t=59s, they send 5 more: only 0 are allowed because the last 60 seconds already contain 100 requests. At t=61s, the first 60 requests fall out of the window and the client can send again. The limit is enforced continuously.
Where it fits versus the other algorithms
| Algorithm | Strength | Weakness | Best fit |
|---|---|---|---|
| Sliding Window Log | Strictest enforcement and fairness | Highest Redis memory and ops | Billing, login/OTP, SLAs |
| Token Bucket | Burst-friendly | Less strict spacing | Public APIs |
| Leaky Bucket | Smooths output rate | Queuing and rejects under load | Backend protection |
| Fixed Counter | Fast and simple | Boundary spikes | Internal traffic |
Configuration in this project
rate-limit:
fallback:
algorithm: SLIDING_WINDOW
maxRequests: 100
windowTime: 60000windowTime is in milliseconds. maxRequests is the count allowed in any rolling window of that duration. If you raise maxRequests dramatically (for example, thousands per minute), Redis memory and CPU grow because every request is stored until it expires. For very high limits, consider Token Bucket or Fixed Counter instead.
Redis state and TTL
Sliding Window Log uses a sorted set of timestamps:
key: rate-limit:{ip}:{endpoint}
zset members: timestamps
ttl: 2 * windowTimeTTL is set to twice the window so old data is cleared even if the key stops receiving traffic. Keep windowTime realistic for your traffic shape because memory scales with the number of requests per window.