Users will shorten a URL and then access it potentially multiple times a day.
Based on this we could say that a user in average will shorten 1-2 URLs per day and access them at least 8 times during the day (case considered is average works hours, accessing to the link once an hour).
With no user authentication we might have more "walk-ins" meaning that we can have traffic in the tens of millions in DAU if the system is somewhat popular.
This means that if we have ~10M DAU, we will have:
If we were to have a URL peaking, and say 50% of the DAU were to visit at least four times a day, we would have:
Finally, if we have 15M unique URLs shortened per day total, this means that in a period of a year we could potentially have 5.475 billion unique URLs shortened stored in the database. In a period of 10 years, we would have 54.75b unique URLs.
This means that if we were to set a hash length of 10 characters using base-62 encoding, we would be able to hold ~800q unique URLS. If we were to set 9 characters as the size, the unique URLs space gets shrunk to ~13q. It would take hundreds of years to even fill that space which reduces the possibilities of collisions.
Just storing 5.475b, 9 character strings will represent 54.75b Bytes (50.73 GB) per year stored in the database.
Thankfully, this is within the limits of what a DBRMS system can hold.
POST /shorten/
DELETE /shorten/
GET /{hash}
URLS (285B)
The table should hold ~100-200b records in typical DBRMS (like MySQL or Postgres) which give us ~20 years of storage.
The system should be composed of
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...
Given the birthday paradox, even seemingly remote, there is still a chance of collision depending the amount of records we store, which becomes greater as the table gets populated.
For this length of a string we would expect to see collisions as the numbers get close to the limit (~13q). Considering that in 100 years we would probably reached 547.5b unique URLs, the chances of reaching 13q records are very low, but still possible. In order to deal with collisions I propose two solutions:
1) Recommended - To hash the URL using base-62 and truncate it to 9 characters. If there is collision, then we truncate the hash to 10 characters and if that collides, we truncate it to 11 characters, and so on. We will use a highly available / partition tolerant NoSQL database for storage:
2) To hash the URL using base-62 and truncate it to 9 characters. If there is collision, then we truncate the hash to 10 characters and if that collides, we truncate it to 11 characters, and so on. We would store using DBRMS:
3) To generate an autoincremental ID and hash it using base-62. We would have to use a DBRMS to save time on the business logic.
In order to deal with URLs that have already been hashed, we have to consider that we have to allow the system to also assign custom slugs to URLs, even if they are hashed.
We have some alternatives:
1) Recommended - Allow for duplicates for fast performance. Hash and slug can be stored under the same column, and it will be already indexed.
2) Create a slugs table, that contains a slug and a FK to the hash in the URLs table. We would create a unique index for the URL.
3) To add a column "is_custom: BOOLEAN" to the URLs table, and add a unique index for (URL, is_custom), then we can implement our business logic in a way such that if there are duplicates, we catch them and return the original record instead.
1) Recommended - LRU, mainly because people tend to access more to recently visited websites, which this eviction strategy uses as a leverage.
2) LFU - If we were to go with this one, it would be mostly useful on peak times, but as soon as that passes we will have old items that peaked lingering until there are newer items peaking in access, which doesn't help too much with the performance.
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?