Estimate the scale of the system. Consider daily active users, read/write ratio, storage requirements, bandwidth, and any relevant QPS calculations...
payload: {"long_url": "www.example.com", "alias": "optional", "expired_at": "optional_expiration_time"}
payload: {"long_url": "www.example.com", "alias": "optional", "expired_at": "optional_expiration_time"}
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
The core entities of this system are:
For this system, we will use a NoSQL database because it is read-heavy. The ratio of read to write will be approximately 100:1. Considering this, a NoSQL database like dynamoDB should be an optimal choice for this. All redirect GETs use DynamoDB's strongly consistent reads to prevent 'not found' errors immediately after creation. We will have two data models, one for storing the URL mappings and another that we can keep for user information.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
The Unique ID Generation service:
This will work based on a unique global-level counter value. To implement this in a distributed system, we can use Redis, which runs on a single thread and operates atomically. The INCR function of Redis will always increase the counter value by 1. Every time a URL shortening request is received, the write service will take the counter value and encode it with base62 encoding. This counter logic will introduce a security issue as any one predict the encoded URLs in the system. To tackle this, we can use a random string to create an XOR with the counter value. We could have also used the encoding after taking a hash of the long URL, but that would create the chance of duplicates in a large amount of data, and we would have to retry multiple times with another hash. On a large scale, this could be non-negotiable.
Now, if the load is increased and we need to horizontally scale the write service, then, in this case also, maintaining Redis as a global counter, there will be no chance of duplication. There could be a question of introducing the network call in each write operation, but that network call will be very fast. If we want to avoid this many network calls, then we can ask for a batch counter from Redis. For example, an instance can ask for counter values from 1 to 10000 in a single network call, and if another instance is calling Redis, then that instance will receive the counter values from 10000 to 20000. Once the counter is exhausted in Redis, the instances can request another batch. This way, instances can keep the counter in memory and perform writes faster.
The Cache Layer Designs:
For ultra-hot keys, CDN and Redis absorb the load before it hits DynamoDB. And DynamoDB's built-in replication naturally distributes read load for hot partitions Handle Failure Scenarios:
In case there is a cache miss at the CDN layer, then the request will reach the backend server, and there it will look for the entry in the Redis cache. If there is a cache hit at the Redis level, then it will immediately respond; otherwise, in case of a cache miss, it will fall back to a DB lookup. Data will be fetched from the DB, written back to the Redis cache and returned to the client.
To handle the cases of the database being down, we can have database replicas, which are provided by dynamoDB. We just have to make sure that our primary server should be able to call the replica copy of our DB without any problem in case the DB is down.
Partitioning:
We will be using the short_code as the partition key, as it is generated from a large space to make sure it is always unique. This uniqueness and partition key will make sure each redirect hits exactly one shard in case of a cache hit via Redis, and in case of a cache miss via dynamoDB