Requirements
Functional Requirements:
- Create a short URL for a given long URL.
- Return the long URL associated with a given short URL.
Non-Functional Requirements:
- low latency redirect p95 10 ms
- low latency creation of short url p95
100 ms
- scalable upto 1 billion overall urls
- fault tolerant
- analytics on urls.
- availability over consistency
- high availability
scale
200 url creations per second
20000 url reads per second
overall 1 billion urls.
API Design
post /urls
{
longurl
} -> StatusCode, shortUrl
get /orignialurl/{shorturl} -> longurl, 301 status code
High-Level Design
client will call api gateway. the creation of urls will go to create service and read calls will go to read microservice. One postgres instance will be used. It will have one table containing urls longUrl, shortUrl mapping. Shorturl will have a secondary index for easy retrieval of long urls. 20000 reads hitting a postgres might be too much. We will configure a redis cache to serve reads. Redis cache will be updated on read. Redis size for 1 billion urls will be around 20 GB which will fit in one instance. Majority of the read calls will be served by redis.
For creation, we will use base62 conversion. We will convert a number to a base 62 number. This number will be provided by redis. Everytime a creation call comes, we will go to redis, update the counter, convert it to a base62 number and then save it to database. Primary key is a combination of longurl, shortUrl in the datbase which will make sure every row is unique.
Detailed Component Design
Creation service will get a counter from redis. In redis, we will set a counter and increment it on every creation call. We will use redis INCR command for that to increment redis counter. We will convert the counter to a base62 number and then this number will be the tiny url like https://tinyurl.com/{base62number}. This will be saved in the database. Redis will have peristence configured with append only file. creation service volume is not high so we will deploy 3 role instances and one of them would be active. Read service will get autoscaled based on the requests thats are coming. 20000 read calls per second can be an issue for the postgres. So we will use redis and store the short Url to long url mapping in redis key value store. One redis instance should be able to handle 20000 read calls. 3 nodes will be deployed in the redis for failover.
There will be rate limiting at a user level to prevent abuse of the service. Also there will be rate limiting at a url level. the quota per short url to get the long url. We can use redis senitel setup for storing counter as well as short url to long url mapping. Postgres will have master slave replica setup. If the master replica goes down slave will be made primary. Postgres will save shorturls created and will be mostly used in creation path. When read call reaches read service, it will check in redis, if not found in redis it will go to datbase to fetch the row. Ratel imiting will be done and we will return retryAfter interval to make sure clients can backoff with retry after intervals. When a call to creation service comes, it will go to redis to get a counter, the coutner will be converted to base 62 and shorturl will be formed. shorturl will then be saved in postgres in urls table. urls table will contain longurl and shorturl. a secondary index will be defined on short url to fetch the long url.