Functionality:
Rate limiter must be as fast as possible. Because we don't want the rate limiter the add a bit amount of latency to our server responses.
And it must utilize the resource very efficiently. Because we want the rate limiter to handle a huge request load.
The system will consist of these components:
How can we identify the user that makes a request. I consider we may use this data:
We will use these points to identify the user. We can join these strings and make a md5 hash function of it. The resulting string will define the specific user. It will be the key in the Redis storage. And the value will be the time series of the user's requests (native time series in Redis). Not updated time series in Redis will be deleted from Redis
So database schema in the Redis key-value:
Rate limiting rules
The rules may be configured using configuration file for the rate limiter service. It may be yaml file. The rate limiter will load the file on the start-up and use it's data as rules. In this scenario the file will contain the amount of requests allowed by the single user (10, 50k, etc.) in a defined period of time (1s, 10s, 5 minutes, etc.). So there are only two parameters in the configuration
Rate limiting algorithm
For example, the rules are 10 requests in 1 second and 50 requests in 1 minute.
Edge cases (failures and scaling)
What happens if the rate limiter restarts?
Here are no any problems with it, because it is stateless and must handle all the requests correctly after restart. The load between rate limiters will be balanced using round-robing algorithm
What happens if there is a failure in the Redis database?
We have to use Redis asynchronous replication that will increase our availability. In case of failure one replica will take place of the master and the rate limiter will start to work again
How can we handle a huge amount of requests?
The rate limiter itself may be easily scaled horizontally, because it is stateless. So the best way of scaling the Redis is sharding using Redis Cluster. The sharding key is the hash again and the algorithm of sharding is consistent hashing. If the load grows, more shards may be added to handle the requests.
Concurrent requests
The concurrent requests in this system will be handled by the Redis. Redis works in a single-threaded mode on a key. So all the concurrent requests will be processed in a row, because the rate limiter will wait for Redis to process the previous request
Multiple users
Our system works well for different users, because we have different keys in the database for different users. So the rate limiting rules will work separately for different users
Concurrent requests from different users is not a problem at all, because they will have different keys in the database. So they will not be concurrent.