Traffic:
The data is small but we can have many users, so we must support this traffic, and system must handle traffic spikes by adding more servers.
Storage:
Each url contains _id, original_url, created_at, user_id(optional)
this is comfortably within Mongo free tier and can be scaling for more users.
Read vs write ratio:
The system is heavily read-dominated, can be 100 reads per 1 write, this is why system should introduce cache system, that will cache urls in the redis and read it from mongo only if its not in redis
Growth projections
With many new URL's per day the system should have optimized read path, cache should be implemented, Mongo collection should be indexed, and should add TTL for old urls (all not used for 1 year urls should be removed)
REST API definition
create_url(original_url):
check if original_url already exist in mongo database - if its exist system does not create new mongo document and just retrieves the existing one and returns it to the client
if original_url does not exist, system creates new document in mongo { _id, original_url, user_id, created_at } _id is the string that represents the unique id for the short url
Example: the domain is create-url.com the short url for the user will be create-url.com/9huUdasf
response is json { "shortUrl": value }
Redirect API:
GET - :id
on get request to the domain name create-url.com/:id, the system should retrieve original url, first trying to get it from cache by id param and if it did not find should retrieve mongo document.
If url is found should redirect to the original url, if it did not find should return error 404
All traffic first hits a load balancer that terminates TLS and routes requests to the appropriate service instances horizontal scaling — multiple server instances behind the LB, auto-scaled by CPU/load
There are 2 main component - create url and redirect to url
In first component create_url - user can enter any url and create shorten version for this url, user sends POST request with url value, first system validates if url already exists it should return the existing url to the user. If it does not exist system insert new document in Mongo database.
The important part of the first component is how system creates unique short Id by generate a unique numeric counter and encode it in Base62 so the ID is compact and collision-free
Second component is redirect flow - user send :id first system tries to finds it in Redis cache if it did not find in cache it try to find it in mongo. If there is no result system will return 404, if result found, first we store short_id -> original_url in cache redis and after this we redirect user to the original_url.
Database that used in design - Redis and MongoDB
Redis will save key value pairs - shortID -> originalURL - we dont want its storage to be more than 10GB so the system should remove old entries when it comes close to 90% of 10GB
MongoDB will have only 1 collection -
the name of the collection is short_urls
schema is: { _id: string, original_url: string, user_id: string, created_at: Date }
index will be on unique _id desc - _id is the shortId
We add TTL of 1 year for the documents by TTL index for created_at.
We add autoscale memory and cpu in Mongo atlas
There are 2 main component - create url and redirect to url
In first component create_url - user can enter any url and create shorten version for this url, user sends POST request with url value, first system validates if url already exists it should return the existing url to the user. If it does not exist system insert new document in Mongo database.
The important part of the first component is how system creates unique short Id by generate a unique numeric counter and encode it in Base62 so the ID is compact and collision-free and add Random Offset to ensure it can not be guessed
Second component is redirect flow - user send :id first system tries to finds it in Redis cache if it did not find in cache it try to find it in mongo. If there is no result system will return 404, if result found, first we store short_id -> original_url in cache redis and after this we redirect user to the original_url.
Shard on a hashed short ID (_id) so every redirect routes to exactly one shard — no scatter/gather across nodes.