List functional requirements for the system (Ask interviewer if stuck)...
Estimate the scale of the system you are going to design...
Average comment size: 1KB
Thread: 100,000 * 1KB = 100MB per thread
Define what APIs are expected from the system...
POST /comments/{threadId}
GET /comments/{threadId}
GET /comments/latest/{threadId}
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...
Schema:
id: id
node: {
commentId: string,
authorId: id
children: pointer to children[]
}
Use a graph database to store our comments as nodes as it resembles a tree structure. Child comments would be added as children nodes to the parent. Graph databases also allow nodes to be stored in order of creation timestamps, which helps us ordering in reverse chronological.
We will also index the graph database to provide faster reads as it's loaded on memory. Writes will be slower, as the database will need to make changes to the index as well. However, this is okay since the system is generally more read heavy.
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...
flowchart TD
Client[Client]
LB[Load Balancer]
LB --> Q[Queue]
Q --> PS
Client --> LB
PS[Comments Service]
NTFER[Notifier Service]
PS --> NTFER
NTFER --> LB
PS --> DB[Database]
PS --> CHE[Cache]
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...
Getting new comments:
A user will maintain a connection to the servers using a websocket via endpoint. Websockets allows the prolonging of a connection between the server and a client.
GET /comments/latest/{threadId}
sequenceDiagram
Client->>+Server: Any new messages?
Server->>+Client: Yes, here's a message
Client->>+Server: Any new messages?
Server-->>+Client: Timeout, connection closed
Client->>+Server: Any new messages?
When new comment gets added to a thread, the post service will update the database first, then the redis cache and then finally send a message to the notifier service, which will send the new comment to the client.
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...
To scale our database storage, as more threads are created, we can partition them by a threadId range. This allows our database layer to be horizontally scaled. A queue is then assigned to each partition and only sends messages when the partition is available. To ensure high availability of the database layer, we look at using a master-slave replication. The master will have the ability to have read/write operations while the slaves will only allow read operations. This also allows our system to be more read heavy reliant. When the master is unavailable, a slave will be promoted to the new master, hence allowing our system to be highly available and continue with writes.
Our cache will be a Redis key value store, where key: threadId and value: comments object. Since popular threads are read more often, we will have less cache misses and in effect, have more efficiency in the reads. We also prevent unnecessary amounts of requests going to the database, which is slower than a cache.
Explain any trade offs you have made and why you made certain tech choices...
Try to discuss as many failure scenarios/bottlenecks as possible.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?