List functional requirements for the system (Ask the chat bot for hints if stuck.)...
List non-functional requirements for the system...
Define what APIs are expected from the system...
GET /userStatus(userId, queryingUserId)
GET /friendsStatus(listof(queryingUserId))
returns listof({userId, status})
PUT /userStatus(userId, status, timestamp)
Enum Status:
ONLINE, IDLE, AWAY, OFFLINE
PUT /statusVisibility(userId, visibilitySettings)
enum VisibilitySettings:
PUBLIC, FRIENDS_ONLY, PRIVATE
Get /activities(userId)
Array of object:
{page_url, timestamp}
WebSocket endpoint:
/websocket
The websocket keep alive ping sent from client to server (to make sure server knows it is still online):
{ "type": "ping", "timestamp": someTimestamp }
The status update of friend status to clients: // Only fires when there are actual updates, and in batch.
{ "type": "presence_update_batch", "data": [ { "userId": "user_789", "status": "online", "timestamp": 1719768900 }, { "userId": "user_404", "status": "offline", "timestamp": 1719768905 } ] }
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 websocket 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 websocket 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.
When a user comes online, it establish a websocket connection with realtime service. Once the realtime service knows it is online, it will asks presence service to send a notification to the subscribers who might be interested in this user's presence data. Presense service then update the presence database with the new status. If it detects a status update (offline -> online), it will then proceed to send the notification to the subscribers through WebSocket.
In order to handle the situation where a connection is unstable and the keepalive signals are not always reliable, we should set some TTL, say, 1 minute, for active status. Only if we do not get keepalive signal for longer than the TTL, we should consider the user offline and update their status (and send out notifications to friends).
The presence database should track a userId's status using a set of connection Id, in order to handle the situation where a user is using multiple devices. When a device is disconnected (i.e. after TTL), that particular connectionID should be removed. A user is only considered to be offline if that connectionId set is empty.
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.
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.