Below are the number of url that we will have to store in our database for a 5 year period: 30 million * 5 years * 12 months = 1.8 Billion
Let’s consider we are using 8 characters to generate a short URL. These characters are a combination of 62 characters [A-Z, a-z, 0-9, _ ], something like http://ad.com/abXdef21. This will give us a total of 3.2 Trillion unique combinations.
Let's try to understand how much storage we will need to store the data for 5 years.
We would want to have 2 types of APIs - one API would serve to get the request and the URL.
The API would redirect the sender to the actual resource and on success return 302 (redirected) or fail on something like 404
The other API would let the user encode their URL in the UI portal. We should allow them to specify the URL themselves which can be hashed easily to locate where the URL is stored
We may also want to ensure that the links ecnoded aren't malicious or point to dark web/denied reosuces. If a user tries to encode something like that, we may deny the request on create API flow.
The ideal database for this system would be key-value value based. Because each URL will have a unique hash we can use that as our key. A NoSQL database like DynamoDB would work perfectly for this design since it is a key-value based database with scalability built in.
Overall, I am okay with after the resource is added, that it takes some time for it to fully propogate to all DB replicas because eventual consistency is OK. However, we want to keep durability (write requests) and latency (making redirect as quickly as possible)
Trade-offs I am opting to use a write-through cache strategy that prioritizes reads over writes. I think this is a fair trade-off given our read-to-write ratio
Another trade-off we are making is around consistency for latency. Since we have chosen db replication to be asynchronous it is possible not all information datapoints will be immediately available to all replicas. However, I think this is a fair trade-off since users may not share or use the link instantly.
Some failure scenarios
Bottlenecks
Instead of using a write-through caching strategy, I can opt in for a write-back strategy where we write to the cache immediately and asynchronously write to the DB. This will vastly improve our write latency, but will introduce complexity in adding an asynchronous process. If the asynchronous process fails, then we will have inconsistent data in both
Additionally, since our system is only eventually consistent, there is a few scenarios where this may not be acceptable. Let's say there is a very famous person who posts a link and within seconds we get 100s of thousands of requests. There are 2 problems here now:
the shortened url hasn't been replicated to all db nodes (especially the ones across the world);
we are getting a thundering herd problem - all of these requests are going to cache miss and all go to the disk which is slow and can cause additional problems/cost in our DB usage.
We can solve this problem by using a "push" based approach in cache for famous/reserve users - we first write in cache and then we write (and replicate) on disk. That would solve the replication/and thundering herd problem, but only if the cache daemon on the application server can have the ability to precisely determine the node id where the cache node with the resource is located.