Skip to content

Upgrade rate limiter from fixed-window to sliding-window to eliminate 2x boundary bursts #787

Description

@pavsoss

What problem does this solve?

src/lib/rate-limit.ts uses a fixed-window counter (as its own docstring notes:
"If we ever need true sliding window precision, swap the backend without touching
callers"). Fixed windows have a well-known flaw: a client can send limit requests
at the end of window N and another limit at the start of window N+1 — 2x the
intended rate in a sub-second span
. For endpoints protected with STRICT (10/60s)
or HOURLY (5/3600s), that doubling defeats the protection these limits exist to
provide (webhook retry, leaderboard scopes, invite sending).

Proposed Solution

Add a sliding-window algorithm behind the existing rateLimit() API so no call
site changes
:

  • Add a rateLimitHitSlidingWindow(key, windowSec, limit, now) method to the
    CacheBackend interface and implement it in all three backends
    (MemoryBackend, UpstashBackend, IoRedisBackend):
    • Redis backends: sorted-set approach — ZREMRANGEBYSCORE to drop timestamps
      older than now - windowSec, ZADD the current request, ZCARD to count, and
      PEXPIRE the key. Pipeline the commands for atomicity.
    • Memory backend: keep a bounded timestamp list per key with the same eviction
      logic (deterministic for tests).
  • Switch rateLimit() to use it (keep the fixed-window path available behind a
    flag if the maintainer prefers a gradual rollout).

Tests

  • Boundary test proving the old 2x burst is now rejected (send limit at t=59s and
    limit at t=61s within a 60s window → second batch blocked).
  • Eviction test (old hits age out and free capacity).
  • resetAt/remaining correctness across all three backends.

Metadata

Metadata

Assignees

Labels

backendBackend / API / DB relatedtype:performancePerformance improvementtype:securitySecurity fix or improvement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions