We can choose either relational DB or NoSQL. We will have a simple schema in a relational DB. Since we do not need strong consistency, we can adopt NoSQL databases.
The database values are fairly simple:
We will partition the DB based on the hash values range. We will use the hashing to reduce the possibility of hot keys. To further improve even distribution, we can do consistent hashing with virtual nodes. Consistent hashing also helps with addition and removal of database nodes.
We expect equal number of reads and writes per second. So we can go ahead with leaderless replication and need to ensure quorum of reads and writes is achieved. We could have used single-leader replication, but in the light of the fact that it is not a read-intensive system but a equally read and write intensive system, leaderless replication is probably better.
POST flow:
The client sends a long URL for shortening.
The long URL is hashed to create a potential shortURL. If the shortURL is already present in the DB, check to see if the longURL matches. If it matches, return to the user an success with payload stating that the URL has already been shortened. This can be shown at the client. If hash is present, but long URL does not match, then add 1 to the hash (linear probing) to check if that hash is taken or not. Continue till a available hash is found.
Once a valid hash is found, add that to the database.
PUT Flow:
In case that this is a PUT request, check if the shortURL is already taken with another long URL, and if so, return an error.
Otherwise, delete the old shortURL, longURL entry and add this new entry. Return Success to the user.
GET flow:
The client receives a short URL.
The client checks if the short URL is present in the cache or database.
If so, the long URL is returned with status code 301
Otherwise an appropriate response with 404 code it given to the client to say that the short URL does not exist in the system
There are a few interesing parts here: The database partitioning and the hash calculation.
Database paritioning:
Hash calculation: