I am trying to use Redis Rate limiting patter as specified in https://redis.io/commands/incr under "Pattern: Rate limiter 1". But how can I scale this in case I want to do rate limiting across multiple servers. Like I have service deployed across 5 servers behind load balancer and I want total requests per api key across 5 servers should not cross x/sec. As per redis pattern I mentioned , the problem is that if I have my rate limiter running in multiple servers itself, then the two different request to two different rate limiter servers,can do "get key" at same time and read same value, before anyone updates it, which can probably allow more requests to go.How can I handle this?I can obviously put get in MULTI block, but I think it will make things a lot more slow.
3
Answers
You need to run LUA script that will check rate-limiting and increase/decrease/reset the counter(s).
You can find a simple example in Larval framework here
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Redis/Limiters/DurationLimiter.php
INCR replies with the updated value. So it can be used as both write and read command.
The accepted answer is incorrect. It leads to inaccurate counter value. Let’s look a the following example:
5 clients executing 5 concurrent requests to Redis. The current state of the counter is 10, and while the limit is also 10.
The 5 concurrent requests will increment the counter to 15 while declining each of the requests. Rather, the value should remain 10 to reflect on the correct number of times clients were "allowed".
Solution:
we effectively needs to combine two separate atomic operations into a single atomic operation. That’s where LUA script comes in. It’s simply a modification on Redis it self to introduce another code path that "executes a get, and then a set" atomically. And it does so because Redis is single thread.