I am assuming no authentication requirements, anyone with access to the website is able to shorten a link.
The api should have 2 main methods: one to add a link to the url shortner, and a second one to get the original link from a shortened link
/shorten/ POST - body: {originalLink: string}, result: {shortLink: string}
/original-link/$shortLink GET - query: {shortLink: string}, result: {originalLink: string}
In addition, an administration page should view all current links and their shortcuts, edit them, as well as remove any they'd like. That implies the following:
/links/ GET/filter - query: {page: integer, filter: string}, result: { mapping: {shortLink: string, originalLink: string}[] }
/links/ DELETE - body: {shortLink: string}
/links/ PUT - body: {shortLink: string, newShortLink: string, originalLink: string, newOriginalLink: string}
The admin page needs some sort of auth, which we will implement using DB authentication levels and a localhosted website that is unavailable to the public. That means the 3 additional API's nested under links are not available from the server.
3 main components: Server, Client and Database.
Database keeps track of the links and their shortcuts. 1 relational DB table should suffice, with the shortLink as the primary key to ensure quick searching for the link its a shortcut for.
In addition to the Postgres Database, redis will be used for caching, using a LRU cache eviction strategy to minimize cache bloat.
Server for interacting with the DB, implementing the API, and for encapsulation.
Will implement a hashing function with salt to generate shortlinks, to ensure uniqueness and prevent prediction of shortlinksj.
Client for creation of links and renavigation when using them. Will have a page for adding links, and will route every shortlink request to its destination.
Additional client for the admin page, not deployed publiclly.
Load balancer such as nginx to manage duplicates for high availability, acts as horizontal scaling. Using a cloud provider such as AWS or Azure, use dynamic scaling for adding duplicates when traffic gets larger. Also add a rate limit for different ips to prevent malicious users, and a larger one globally to prevent ddos attempts. The global rate limit should be elastic to account for natural increased usage over time.
The communication is as the graph on the right displays, a load balancer that communicates serves the client static files and serves requests to the server, the client accesses the API through the same route which accesses the server indirectly, and the server accesses both the redis cache and the Database.
# Deep dive to the Database
Choosing Postgres out of familiarity.
1 Table for the shortcuts and their destinations. An index on both rows (default index on the primary key)
3 users, SUPERUSER that is unused in the application, SERVER with write and read auth, ADMIN with full auth to the table.
The server has access to the SERVER user, and will implement the 2 api calls to add and follow a link.
The client has no access to any DB user, will only use API calls.
The localhosted admin page will have access to the ADMIN user.
Sharding will be used when the database becomes extremely populated. The sharding strategy will be taking the hash of the shortlink to try to evenly sprad writes over the different shards. Before introducing the sharding, introduce read replicas to account for high write volumes.
## Cache
The cache will be implemented using redis. The cache is managed using a LRU eviction strategy, using a maximum size of 1000 active links cached at once. Once a link is used, evict the least recently used link (bottom of the queue) and add the link. Each link has a latest-used timestamp field, and sorting around that field gives us the LRU order. The cache update for each link use will happen after returning the link to minimize latency.
When a link is edited or deleted, remove it from the cache.
# Deep dive to the client
Simple web page, no room for expanding much, can be done with pure html and javascript to avoid bloat.
Main route will be composed with an input for the link you want shortened. After pressing submit, loading will occur until the result comes back, after which it is displayed and copyable.
The shortened links will be accessed through the /link/$shortlink route. Each time the website gets a request, it will first check if the route matches the shorturl regex, then if it does it will send an api call to get the originalLink from the server, and reroute there.
If the server returns a Not Found result, reroute to a special 404 page.
# Deep dive to the Server
Implemntation of the API routes themselves is direct SQL queries.
Short link creation will be handled using sha-256 with the timestamp as a salt on the originallink, truncated to 5 characters. On collision, retry up to 5 times. When the number of links increases, increase the number of characters used for t he shortlink, to account for the limited amount of links that can be used using 5 characters. each order of magnitude of links adds an additional character to the number of characters used, i.e. if there are 10000 links use 5 characters, 100000 use 6, etc.