40,000 user per month (upper limit in growth phase)* 100bytes per row= 4mb per month
4mb * 12 = 48 mb per year for user table
2000 writes per month (upper limit in growth phase)*450 bytes per row= 900kb
1mb (upper limit) *12 =12 mb for a year
The API's will be called after the client has reached the load balancer and the rate limiter. The implementation of load balancer and rate limiter will be discussed in the overall design part.
URL_TABLE:
USER_TABLE:
We will follow a single-leader replication as this design is a heavy-read design. The replication will help with redundancy.Since this is a heavy read application it makes sense to shard the database using consistent hashing based on the long url.
We will use a write-around cache to help with our reads. The write-around is because we assume many of the writes will not be popular, and writing through the cache will probably be wasted. The cache size can be 20% of our capacity to start; based on the access patterns, we may need to increase or decrease the size. We will use the LRU cache to evict since we don't need the least recently used data. For increasing or decreasing the size of our cache we can look at the top items in our cache (most accessed ) vs bottom items. If the size is similar we can increase the cache, but if there is a huge gap in access we can decrease the cache.
Although our design uses a single leader replication we are deploying this across the globe in multiple regions. This will help improve redundancy and improve latency.We will use multi region data replication to improve consistency. Each region will have its own leader and followers and changes would replicate async to other regions. Since it is likely that each region will favor different links we will have region specific cache.
We will use a global load balancer followed by a regional and rate limiter as a point of entry. The rate limiter will help prevent denial of service and ensure the system is available. The load balancers first job is to route based on the nearest server, this ensures faster writes and reads.The load balancer then can either run and allocate resources based on the load on each server or use a consistent hashing protocol.We can implement the rate limiter based on IP address .If the user exceeds the limit we can display a message letting them know. In case of a failure in one region we can have the load balancer direct the traffic to another region.
Writes:
Assuming the client is authenticated, we will write to the leader and update the follower nodes for write requests. To minimize the risk of writing to the leader and not updating followers, we can only return the response when we can read from the followers. To mitigate this issue, we can implement a quorum strategy.So when majority of follower nodes have been updated we will let the client know the write was successful. In the case of a delay or time out we would first use a retry mechanism but if it persists it makes sense to inform the leader about the write not going through and ask them to repeat the process and abort the writes.
Above was assuming that the links are safe to write to our database. As stated we will use third party API's to sanitize the links and make sure they don't have any malicious content. Our reliance on third party apis introduce a single point of failure. To minimize this we can have 5 apis to utilize for our safety checks. This could potentially lead to inconsistent writes because one API could flag a link as malicious but another one could think its ok. In order to mitigate this we can have an odd number of API'S and create a voting mechanism
Reads:
Reads would follow the same protocol regarding load balancing and rate limiting as writes. We should increase rate limits for reads however because this is a heavy read system.The read requests would first have to go to the cache, if its a hit we can return the response and if its a miss we can go to our follower nodes and see if it exists if not return a message that the link does not exists and if it does we update our db and increment the value associated with reads per url.
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...
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...
Explain any trade offs you have made and why you made certain tech choices...
Try to discuss as many failure scenarios/bottlenecks as possible.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?