List functional requirements for the system (Ask the chat bot for hints if stuck.)...
List non-functional requirements for the system...
Estimate the scale of the system you are going to design...
Define what APIs are expected from the system...
set of APIs :
GET /checkAllow
Body : { user, api_endpoint, timestamp of request }
Response : Status 200, Body : True / False and time left for refill if False.
Defining the system data model early on will clarify how data will flow among different components of the system. Also you could draw an ER diagram using the diagramming tool to enhance your design...
Database I use here to keep track of count is redis.
It is a kv store.
I would use INCR on the key to increase the count.
{
key : userid + api endpoint
val : count
}
You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...
Explain how the request flows from end to end in your high level design. Also you could draw a sequence diagram using the diagramming tool to enhance your explanation...
I have drawn the diagram.
Dig deeper into 2-3 components and explain in detail how they work. For example, how well does each component scale? Any relevant algorithm or data structure you like to use for a component? Also you could draw a diagram using the diagramming tool to enhance your design...
there are different algos that can be used and which one to use depends on the use case. In this I have designed the ratelimiter for token bucket where you have fixed number of tokens per bucket (userid+apiendpoint) and each request would consume a token. if the bucket is 0, no api call is supposed to go through and api gateway will return 429. and the bucket keeps refilling at a fixed interval.
From the rate limiting service side, this would scale well because it essentially has no state attached to any one of the vm instances and the part where count is tracked is offloaded to redis to scale independently.
redis would run in cluster mode, where for a given key (user id + api endpoint), val of count is incremented using INCR command.
this should also scale well since, this is stored in memory. use consistent hashing for the key for a evenly distributed load across all redis nodes. Incase redis fails, the api gateway should scale up rapidly and allow only around N requests per endpoint. N depends on the available resources.
Explain any trade offs you have made and why you made certain tech choices...
I have used redis here and redis offers AP and no strong consistency though it can be ensured to make masters wait until a slave acknowledges write. Each write gets written to a master and master replicates to its replicas. There would be inconsistency when there is a master failure or network partitioning, but because redis allows fast access to in memory kv pairs, we have chosen this and once in a while master failure and subsequent extra load should be handled in such a way that redis' availability is well in tune with the extra buffer we have for the api system.