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...
Let's make the following assumption:
Define what APIs are expected from the system...
GET /userStatus(userId, queryingUserId)
PUT /userStatus(userId, status, timestamp)
Enum Status:
ONLINE, IDLE, AWAY, OFFLINE
PUT /statusVisibility(userId, visibilitySettings)
enum VisibilitySettings:
PUBLIC, FRIENDS_ONLY, PRIVATE
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...
User Table (SQL)
userId, Int (Primary Key)
Status, String
User Activities (Redis Set)
userId, Int (foreign key)
last_active_at, timestamp
A redis set is used to keep the last timestamp to support both high read and high write volume.
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...
There are about 5 conceptual components in the system:
Subscriber: This component should be in the client and is subscribed to real-time service, listening to the status events from interested topics (friends). Its connection to realtime service should be a SSE connection. The subscriber should also talk to presence service to get the status of a user.
Publisher: This component should also be in the client, and events such as heartbeat and status updates should be published in the real-time service. Its connection to real-time service should be a SSE connection.
Realtime Service: This is the service in charge of listening to publisher and broatcasting events to subscribers.
Presence Service: This is the service that should be talking to subscriber and directly returning them the initial presence data when a user first start to subscribe to the status of a friend.
Presence database: This is the database that preserves the status of 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...
Subscribe Flow:
When a user first start subscribing to a friend. The following steps happen:
When a user updates their status, the following steps happen:
When a user status timeout, the following steps should happen:
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...
The Presence Service should be used to detect if a client is timeout.
Each time when the Presence Service receives a timestamp about a client, it sets a delayed trigger for the client or reset it if it is already exists. When a delayed trigger is triggered, it will query Presence Database to make sure that the client is indeed timing out. If that's correct, it sends a request to realtime service and have it broadcast the fact that the client is offline.
The realtime service consists of a number of components, a dispatcher, a gateway server, and each gateway server should have an associated subscription store, and there should be a shared endpoint store.
When subscribing to a topic, the subscriber establish a SSE connection with a gateway server. The gateway server will then stores the subscription information into the local memory subscription store, and if this is the first subscriber in this server for the same topic, it will store this into the shared endpoint store.
When a publisher publishes a topic, the publisher first publishes the event to a dispatcher. The dispatcher will query the shared endpoint store to find all gateway servers that's subscribed to this topic, and dispatches the events to all of them. The gateway server will then forwards the event to the subscribers based on its local subscription storage.
Explain any trade offs you have made and why you made certain tech choices...
In the presence database, the last active timestamp is stored in a key-value pair storage redis Set instead of an ACID or other NoSQL database such as Apache Cassandra, as redis supports fast access, very suitable for this use case. This use case also does not require the relational information, ACID… that would require a more complicated implementation.
Try to discuss as many failure scenarios/bottlenecks as possible.
One Failure scenario would be a jittery connection. To handle this, the timeout of a client in the Presence Service should be set to a reasonably long time.
Both presence service and realtime service should have duplicate. If a realtime server is down, both publisher and subscriber connected to it should retry connection.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?
More duplications, or multiregional deployment should be introduced as more users are joining.