Estimate the scale of the system you are going to design...
We would need two APIs, one of type GET and the other of type POST.
Since we are more concerned with reads here and less about writes, we can utilize a database that employs a single leader replication technique. In a single leader replication scheme, the leader would be responsible for the writes and all of the followers would be replicas of the leader that would handle all of the read requests.
Single leader replication is utilized by relational databases like MySQL and PostgreSQL. Among those two, MySQL utilizes row locking to deal with competing processes, while PostgreSQL uses Serializable Snapshot Isolation, which is a form of optimistic concurrency control. PostgreSQL is utilized when we know that we are not going to be running into competing processes, hence, we utilize PostgreSQL as our database.
flowchart TD
B["Client"];
C{"Load Balancer"};
D["Application Server"];
E["Redis Cache"];
F["PostgreSQL Database"];
B --> C;
C --> D;
D --> E;
D --> F;
sequenceDiagram Client->>+Server: Do you have the resource for me? Server->>+Cache: Do you have this resource for me? Cache->>-Server: Yes, here you goo. Server-->>-Client: 302 Redirect Server-->+Database: Do you have the request for me? Database-->>-Server: Yes, here you go Server-->>Client: 302 Redirect/404 Error
We have not considered the thundering herd problem, wherein a short URL can see a sudden spike in the number of users wanting to access it. Such short URLs can be written to cache first and then the database, so that redirect requests can be served using the cache, leading to fast response times and reduced load on our database.