Define what APIs are expected from the system...
To create a shortened url
POST /v1/short_url/create
To update a shortened URL, essentially allows the user to
PUT /v1/short_url/:id
To remove a shortened URL
DELETE /v1/short_url/:id
When you navigate to a shortened_URL it will include an ID that will fetch the redirect URL
GET /v1/short_url/:id
Given the low-complexity of the data structure, and the need of low latency and scalability, I would go with a non relational database such as MongoDB or Amazon DynamoDB.
We would need essentially two entities:
User: id, name, email, urls
Url: id, redirect_url
Essentially, the id is part of the shortened_url like www.service.com/:id
The client connects to the service though the API gateway, we can also include a Load Balancer so we can have replicas of each service and balance them to assure high availability.
Depending on what the user wants to do, they will access either the URL Shortener Service or the Redirection Service.
URL Shortener Service: to create, update or delete shortened urls
Redirection service: When a shortened_url is visited, it will redirect to the stored url.
Both services connect to the same database. We can also have a cache, specially for the Redirection service to decrease the latency. For this we can use something like Redis.
User access a web client that allows to create, update and delete shortened urls. This happens via the URL shortener service
Guests access a shortened url like www.service.com/:id that allows to access the Redirection service which access the database and makes a redirection. Cache is relevant to make it faster.
The database is non relational and scales well, we can shard it by geolocation so the latency will be smaller. Also, we would have replicas of it to minimize downtime.
Url shortener service and redirection service will also have replicas to eliminate downtime.
Explain any trade offs you have made and why you made certain tech choices...
Having a cache can be a problem if for example a URL is updated or deleted. It will impact in the database but not in the cache so a user could be redirected to the wrong url. To fix this, everytime a url is updated or deleted, the URL shortener service will call the redirection service and invalidate the cache for that ID. This way, next time the redirection service calls that ID it will fetch ir directly from the database and write it in the cache again.
If a redirect URL doesn't exist anymore we would redirect the user to a 404 which could look bad on the service. So a possibility is to fetch data from the stored URLs every 60 minutes and if we validate that a website is down we could show a friendly error page to the user to inform the website is down and they can see is not our service failing.
If all guests are suddenly accessing the same URL that could exceed our resources and bring the redirection service down. To prevent that we should limit the number of requests that can be done per minute so we guarantee the service won't collapse and also to protect from ill intentioned attacks.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?
We should add an Analytics service to also have metrics on the URLs metrics that we can share with our users.