List functional requirements for the system (Ask the chat bot for hints if stuck.)...
System Properties
List non-functional requirements for the system...
Estimate the scale of the system you are going to design...
Define what APIs are expected from the system...
Core Entities
Generate a Short Url for a longUrl with optional expiry date and alias name
POST /v1/urls
Request boody: {longUrl, alias, expiresOn}
Response: 201 Created
Response Body: {shortUrl, expiresAt}
Update alias name or expiry of a Url Entity
PUT /v1/urls
Request Body: {alias, shortUrl, expiresOn}
get one or all short urls
GET /v1/urls/{shortUrl}
Response: 200 OK
Response body: {one or more instances of Url}
Redirect a short url to long url
HEAD /v1/redirect/{shortUrl}
Respose: 301 Temporarily Moved
Response Header : {longUrl}
Defining the system data model early on will clarify how data will flow among different components of the system. Also you could draw an ER diagram using the diagramming tool to enhance your design...
You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...
Explain how the request flows from end to end in your high level design. Also you could draw a sequence diagram using the diagramming tool to enhance your explanation...
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...
A 48 bit id,
Base 58 hashing a 48 bit id (6 chars) can generate 38B URLs and when we run out of unique hashes, change the epoch date of timestamp to a recent date say 01-01-2027 00:00:00, this will give another 38B unique URLs.
In this case we can horizontally scale no of nodes that generate unique short codes for urls.
Browser of client ask for a long url in a head request which is lightweight and faster than a GET request. The request url contains the short code, this is looked up against a in-memory cache, if the cache misses the the redirection microservice goes to database and pull the url, add to cache with a TTL (LRU eviction strategy) and returns the URL in request header with 301 HTTP response code. We dont want to return 302 HTTP, since this gets cached into CDNs or ISPs. In this case we cannot mark the no of redirections performed for a short code.
The redirection service will experience heavy traffic and when the url becomes viral, this will introduce spikes and redirection service goes under heavy load. Redirection service is stateless and we stand up multiple nodes of Redirection service (auto-scaling if lambda/function or auto-scaling pods in Kubernetes deployment) helps here.
A Memcache is introduced to cache ShortUrl=LongUrl, memcache is distribute cache and helps in standing replica in multiple regions and syncs between the cache. We need a simple Map data structure here. This can helps in handling read spikes and viral urls
The multi-region data redundancy is ensured to handle disaster recovery, the database nodes are deployed across multiple regions with asynchronous less than 1 sec replication. A master node takes the writes (300 writes/sec) and these gets replicated to 2 nodes making it 3 nodes in the database cluster. The 2 nodes are deployed in different regions. In case of master node going down, any of read replica can be promoted to master node within 1 sec. The master node can be brought up reclaim its membership.
In each region, the data is replicated to 2 other nodes to ensure 3 copies exists in a region. This means total 3 regions with 2 copies each making it 6 copies of a Short URL record.
the data is replicated asynchronously with in a sec, this means the URL generated at Reston, VA may not be available immediately on Europe region, however this is expected and 4xx error on a short URL is acceptable for upto 1 sec
Url record is partitioned by generated Short url and sortable by unique Id (twitter-snowflake id). This makes the data residency spread across multiple partitions in master and read-replicas, avoiding hot partitions. The cache also uses the same strategy making it spread across regions and partition data in each region.
Data in database is replicated to 5 other nodes, which are read replicas. These 5 nodes take in only reads and denies writes unless one of these nodes are elected to be a Master node. Only master node takes the writes. On top of database, a caching layer is introduced for fast lookups hence the no of queries to database is reduced.
Explain any trade offs you have made and why you made certain tech choices...
Try to discuss as many failure scenarios/bottlenecks as possible.
The short URLs are guaranteed to be unique even though its produced by different nodes, however when the date runs out it can produce a duplicate, we introduce a low latency duplication check using bloom filter in cache. However this has to be in each node, which might be not effective.
The master node going down have an impact and delay for writes since one of the read-replica needs to be elected as master. However this delay or impact is within a sec, with 300 write requests/sec this is acceptable
Read replica going down is handled by health checks and the requests are not forwarded to failed node.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?