Skip to content

fix(server): share API rate-limit budget across replicas via Redis - #838

Merged
Mikey-222 merged 1 commit into
Hel-Phone:mainfrom
UltimateUser1:fix/issue-752-shared-redis-rate-limiter
Oct 18, 2026
Merged

Mikey-222 merged 1 commit into
Hel-Phone:mainfrom
UltimateUser1:fix/issue-752-shared-redis-rate-limiter

Conversation

@UltimateUser1

@UltimateUser1 UltimateUser1 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The general API rate limiter kept its request counters in per-process Maps. On Render with multiple replicas, each instance enforced the limit independently, so one IP could send up to N times the configured budget (N = replica count), and per-replica 429s looked like client flakiness.

This PR moves the counter into Redis when REDIS_URL is set, so every replica shares one budget.

Changes

  • server/index.js: createRateLimiter uses an atomic Redis fixed-window counter (INCR + PEXPIRE in a single Lua script, so the TTL is always set) when Redis is configured.
  • Without REDIS_URL, the existing in-memory path is unchanged, so local dev and tests behave as before.
  • New optional keyPrefix option keeps counters for different limiters separate.

Failure semantics

  • Redis errors fail open and log [rate-limit] Redis unavailable, failing open. Blocking emergency traffic during a Redis outage is the riskier choice, and the log line is the alerting hook.
  • Whitelisted callers still bypass the limiter before any Redis call.

Testing

Not run in this PR. Suggested check: start 3 instances against one Redis with RATE_LIMIT_MAX=100, send 300 requests from one IP across them, and expect about 200 responses with 429.

Closes #747
Closes #752
Closes #756
Closes #757

@drips-wave

drips-wave Bot commented Oct 18, 2026

Copy link
Copy Markdown

@UltimateUser1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Hel-Phone#752)

The general limiter kept per-process Maps, so with N Render replicas an
IP could send N times the configured limit. When REDIS_URL is set, the
counter now lives in Redis (atomic INCR + PEXPIRE via Lua). Without it,
the in-memory path is unchanged for local dev and tests.

Redis errors fail open and are logged, so emergency traffic keeps
flowing during an outage.
@UltimateUser1
UltimateUser1 force-pushed the fix/issue-752-shared-redis-rate-limiter branch from 94a500b to 4959dc1 Compare October 18, 2026 18:39
@Mikey-222
Mikey-222 merged commit adec337 into Hel-Phone:main Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment