Rate limiting sounds simple until two requests hit the check-and-increment step at the same time. Here’s the sliding-window limiter we ended up with.
Why not INCR and EXPIRE
A naive INCR + EXPIRE pair isn’t atomic across two round trips — a crash or a slow network between the two commands can leave a key that never expires, quietly locking a client out forever.
One round trip, one script
We moved the whole check into a single Lua script executed via EVAL, so the read, compare, and increment happen atomically inside Redis itself. One round trip, no race window.
Sliding, not fixed, windows
A fixed window resets at each hour boundary and lets clients burst 2x their limit right at the edge. We switched to a sliding-window log trimmed inside the same script, which closed that gap without adding a second round trip.