List functional requirements for the system (Ask the chat bot for hints if stuck.)...
support web and mobile clients
notifications when offline and message was recieved
one-on-one messaging
message groups
messages are text based
nongoal: e2e encryption (followup)
users can be distributed across regions
List non-functional requirements for the system...
5M DAU
10 messages per day on avg
avg message size is 1kb
message size limit 10kb
indefinite storage of old messages
messages are not seen out of order
otherwise, linearizability is not required
Estimate the scale of the system you are going to design...
Highly scalable
1e4 bytes/message * 5e6 users * 1e2 messages/user*day * 4e2 days/yr => 2e15 => 20 PB / yr
2e9 RAM / server * 1/1e4 RAM per connection * 50% headroom => 1e5 connections per server (max group size)
Define what APIs are expected from the system...
# assigns user with a stateful backend server for the websocket connection
login(user_id)
send_message(txt, from_user_id, to_user_id)
send_message(txt, from_user_id, to_group_id)
# push notification:
message_recd(txt, to_user_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...
message storage: KV store (ie: dynamo db or cassandra)
schema:
message_id, message_text
message_id increases over time
user/device storage: relational db
settings for which devices belong to which users, whether notifications are muted, etc
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...
client: web/mobile app with websocket connection over HTTP
api server via load balancer: stateless server to interact with user/device storage and service discovery
service discovery: to use websockets, we need a stateful server associated with a client. When a user logs in, they are assigned one of these servers until they are either disconnected or log out.
chat server: provides websocket connection to handle messages sent/received from online users
notification system: notify offline users when messages are received
message queue: store messages for offline users
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...
user login: through LB, to api server, to service discovery. Once it's assigned a chat server, it will connect there to send/receive messages.
message sent: chat server called id generation to get the new message's id. Message is added a queue, and the queue is consumed by a message worker. Of the destination user is offline, the message is stored in the user's offline queue, else it is sent to the user's chat server so they can recieve the message.
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...
api server: stateless web server behind a load balancer for even load distribution
user/device storage: relational, shared storage
chat server: uses websockets, with replicas to handle all the connections
message_id generator: Use epoch-like timestamps and include the datacenter/machine id to avoid conflicts. Each message is unique and increasing with time.
chat storage: sharded KV storage
Explain any trade offs you have made and why you made certain tech choices...
websockets vs polling vs long-polling:
for bursty, real-time performance, websockets are the best choice for server-initiated requests. But requires service discovery (ie zookeeper) and a stateful chat server to maintain the connection.
Try to discuss as many failure scenarios/bottlenecks as possible.
Large groups - no longer feasible to store a copy of a message for each user in a group.
chat server crash - the crashed server's connections will need to be re-established to a failover replica
database outage: all datastores will use cross-region replication and failovers to minimize data loss.
For the KV message store, new leaders are elected via consensus protocols, and failures are detected via gossip protocols.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?
Support larger groups of users, or streaming.
Support "online presence" indicators