Rate Limiting — Centralised Redis Counter
Centralised rate limiting via Redis INCR
A Redis counter per user, shared across all connection servers, enforces the rate limit correctly regardless of which server the user is on or how many times they reconnect.
The solution#
Every connection server, before forwarding a message, increments a shared Redis counter for that user:
The flow on every incoming message:
Alice sends a message
→ Connection server: INCR rate:user_alice
→ If result > 10: reject message
→ If result ≤ 10: forward message to app server
On the first message of a new second, the key doesn't exist — Redis creates it with value 1 and sets TTL to 1 second. At the end of the second, the key expires automatically. The counter resets with no cleanup needed.
Why INCR is the right operation#
Redis INCR is atomic. Two connection servers can INCR the same key simultaneously and Redis guarantees they each get a unique, correct result. There's no race condition, no double-counting.
Server 3: INCR rate:user_alice → returns 8
Server 7: INCR rate:user_alice → returns 9 (Alice reconnected mid-second)
Server 7: INCR rate:user_alice → returns 10
Server 7: INCR rate:user_alice → returns 11 → REJECT
All servers are looking at the same counter. Reconnecting doesn't help.
Key design#
Key: rate:<user_id>
Value: integer (incremented per message)
TTL: 1 second
Limit: 10 messages/second for 1:1 messaging
The Redis call adds ~1ms latency per message. At 10 messages/second per user this is negligible.
Interview framing
"Rate limiting lives on the connection server. On every incoming message, we INCR a Redis key for that user with a 1-second TTL. If the count exceeds the limit, we reject the message. The atomic INCR means all 500 connection servers share one counter per user — reconnecting doesn't bypass it."