List functional requirements for the system (Ask the chat bot for hints if stuck.)...
List non-functional requirements for the system...
Estimate the scale of the system you are going to design...
10K ids generated per node per second.
Num seconds in a day = 100k
Num seconds in a year = 100k * 365 = 4 * 10^7 approx
Num ids generated in a year per node = 4 * 10^11
Assuming 5 id generator nodes globally, num ids generated in a year globally = 2 * 10^12
If system must be usable for 50 years, num unique ids to be generated = 10^14 = 2^40.
So 40 bits will be required to represent an id.
3 additional bits for representing the machine address.
Total 43 bits
Define what APIs are expected from the system...
GET https://
Returns a unique 43 bit id
Defining the system data model early on will clarify how data will flow among different components of the system. Also you could draw an ER diagram using the diagramming tool to enhance your design...
An id will be represented as follows:
3 bits machine/cluster address + 40 bits id
Id is incremented for every request, updated in persistent cache and returned the user. Redis can be used a persistent cache.
Steps 1 and 2 should be atomic.
A Key-value store DB can be used. 2 pieces of information can be stored as follows.
Key: lastIdGenerated. value: id
Key: clusterAddress value: mac or unique address to identify this cluster. It is part of the id.
You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...
Load Balancer: Used to regulate the number of incoming requests
Auth: Authenticate the user to prevent DDoS attacks
Id incrementor: Increments the 40-bit id part of the id
Redis Cache: Used for fast read/write access of the last generated id. Cache is backed by a key-value store.
KV Store: Stores the lastGeneratedId and clusterAddress persistently.
Zookeeper: Used for monitoring for failed nodes in the cluster and discovering newly added ones.
Explain how the request flows from end to end in your high level design. Also you could draw a sequence diagram using the diagramming tool to enhance your explanation...
Startup:
On startup, the system reads its clusterAddress and lastGeneratedId from the DB and serves new incoming requests using this info.
Failure Scenario:
Dig deeper into 2-3 components and explain in detail how they work. For example, how well does each component scale? Any relevant algorithm or data structure you like to use for a component? Also you could draw a diagram using the diagramming tool to enhance your design...
Nginx server can be used for serving API requests as it is known for fast response time.
Explain any trade offs you have made and why you made certain tech choices...
Generate auto-incremented vs random ids
It is possible to design a system which generates a random id. But with that approach, a set of already-generated ids must be kept in DB so that the same id is not repeated. This increases the storage requirement and throughput suffers due to the additional step to validate newly generated id against existing ones.
Try to discuss as many failure scenarios/bottlenecks as possible.
This is a stateless system. So node failures can be recovered easily by reading lastGeneratedId and clusterAddress from the DB.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?