High-Level Design
Total number of URLs = 2e6 * 365 * 10 ~= 8 Billion URLs
Standalone Design (to satisfy functional requirements)
- Simple design with a client, server (with shortening logic) and a database to store URL maps
- Shortening Logic:
- Prefix / Suffix of the URL:
- Could work if the URLs are from different websites all the tiime.
- Practically, not the case, we can have many URLs from the same website, so the prefix cant be the key.
- Different websites may have different contents and routes, but the chances of having same suffix is highly likely, so cant use suffix here too.
- Hash - MD5/SHA1/SHA256:
- These could work, as they give different hash for different input.
- Even a smaller URL will get a bigger hash. Trimming the hash to first x chars or last x chars, increases the chances of collisions so its not worth it.
- Integer Counter
- A simple integer based counting function could work, for every ask to the function it increments and returns the number
- Could work, but a linear 8 billion integer space, can cause predictability and not safe and secure.
- Base62 based Counter
- Instead of using the simple integer, we can encode it to base62 (0-9,a-z,A-Z).
- If you take 8 chars for short key, i.e. 62^8 ~= 220 Trillion URLs (0.003% of our requirement, no overrun of the uniqueness space).
- So the uniqueness space is quite large, so to avoid predictability we can even add a random seed to the counter.
Storage Estimations
URL Table:
- Short key (PK) - 8 bytes
- Long URL - 200 bytes
- User Id - 8 bytes
- Alias - 100 bytes
- Expiration Time - 8 bytes
- Creation Time - 8 bytes
1 URL ~= 500 bytes
8 Billion URLs = 500 * 8e9 ~= 4 TB
In practice, indexing + sharding + replication - 3-4x ~= 20TB
Scalable Design (To satisfy scalability):
Avg QPS:
Reads = 20 million / day = 24 reads / sec
Writes = 2 million / day = 2.4 writes / sec
Peak QPS:
peak = read - 100x, writes - 50x
headroom = 2
Reads = 2400 reads/sec * headroom = 4800 reads/ sec
Writes = 120 writes / sec * headroom = 240 reads/sec
As the scale is high, we need to move to a staless architecture.
Components:
- LB: Balances the traffic for api gateway and services.
- CDN: Region specific cache.
- API Gateway: Handles AuthN, Rate Limiting, TLS Termination.
- Creation Service and Redirection Service: Stateless services, that can be auto scaled based on the Read/Write QPS.
- Base62 Counter Service:
- As we scale, we cant keep the counter service as part of the creation service anymore, we need a seperate global counter service, which all the creation services will use to get new short key.
- Also to reduce the load as more services ask for short key, the counter service instead return a range that a service can use on it own to create the key and return without consulting the shorten service.
- If a service does go down, its ok as we have enough unique space, we can just allocate a new range and still never go out of scope.
- Cache:
- As we scale, and the system is more ready heavy, we can add a cache, to reduce the load on database
- Assuming a hit rate of 90%:
- Without cache: DB Read QPS - 4800
- With cache: DB Read QPS - 480
- Database:
- Needs to be replicated, in case failure