RL
GitHub
Documentation

Sliding Window Log

Strict rolling windows for accurate enforcement.

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:

  1. Compute window_start = now - windowTime.
  2. Remove all timestamps older than window_start.
  3. Count the remaining entries.
  4. If count < maxRequests, add now and allow. Otherwise reject.
  5. Set TTL to 2 * windowTime to cover late cleanup safely.

Diagram

Sliding window

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

AlgorithmStrengthWeaknessBest fit
Sliding Window LogStrictest enforcement and fairnessHighest Redis memory and opsBilling, login/OTP, SLAs
Token BucketBurst-friendlyLess strict spacingPublic APIs
Leaky BucketSmooths output rateQueuing and rejects under loadBackend protection
Fixed CounterFast and simpleBoundary spikesInternal traffic

Configuration in this project

yaml
rate-limit:
  fallback:
    algorithm: SLIDING_WINDOW
    maxRequests: 100
    windowTime: 60000

windowTime 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:

text
key: rate-limit:{ip}:{endpoint}
zset members: timestamps
ttl: 2 * windowTime

TTL 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.