The system is going to be relatively small with a few scalable parts
There should be an API endpoint to convert a URL.
This is a PUT request where the request body contains the original URL and the expiry time.
It returns the new URL and the expiry time.
There should also be an API endpoint that will preform the URL redirects.
It would handle all requests to some path like https://myservice/url/* where the path after /url/ is the id of the short url
One database will have one table for URLs
Due to the simple nature of the data we'll use DynamoDB, but something like MySQL can work just as well. NoSQL databases wouldn't work as well if, later, we wanted to add features to e.g., show all URLs created by a specific user.
This table will have columns for id, original_url, new_url and ttl.
The primary key will be the new_url.
The ttl value will be used as the time to live.
We'll have another table in our database (this could also be a secondary DB like Redis or DynamoDB) to handle requests. Every time the user makes a request we'll store their ip and the request time.
This table will have columns for ip and ttl.
The primary key will be the ip.
The ttl value will be used as the time to live.
At a high level we want to implement:
To get a short URL
URL redirect lambda
I chose DynamoDB because it has easy access to the concept of ephemeral entries and because it's very fast. Our data is simple in nature with few relationships. This allows us to easily write and forget without managing foreign keys or transactions or scheduling cleanup jobs.
I chose Lambdas because they, like other serverless function technologies, can be easily scaled independently. The usage of the URL generation API will be much less used than the redirect API, therefore it has different scaling requirements. A long-running server may be more cost-effective for our service after we reach a certain level of popularity, but the difference in costs should be relatively small. We would need to do some napkin math to determine if the cost difference is significant for our business.
I chose a vanilla html/css app as our client due to the simplicity and relatively little interactivity we require. This will help us deliver a high quality and simple experience very fast with incredibly little maintenence, but it may not scale well in the long term if we want to continue to build more and more complex features into the platform. This is an acceptable tradeoff for a starting business with a single good feature.
Our service can fail in the following ways:
We can build fault tolerance into the system by having multiple stand-by lambdas and DDB read replicas ready to handle extra requests. These lambdas and read replicas may exist in different regions to mitigate regional instability.
We can also deploy our infrastructure into different regions and have the client choose the closest API for better speeds.
We can also store the current url length in our database and run a scheduled job to update the length. When we start detecting too few urls availble (repeated failure to generate a new url) then we can update the minimum value and temporarily increase our URL size until the older URLs expire.