POST status/{userId}/send -> send status update to subscriber
POST status/{userId}/ (payload is OnlineStatus) -> send a single status update (login, logout) of a user from a given device
High-Level Design
DNS: route the client request to the nearest geo location
Load balancer: distribute the traffic across different API gateway of the region
API gateway: dealing with AuthN, rate limite, route the request to the right service.
Notification service: manage the notification subscription and keep connection with clients.
Status aggregate service: Handle login/logout status update, and calculate the overall status of a given user from the user's devices.
Tracing service: Subscribe to message queue on user login/out updates, then store login/out traces.
Message queue: Async process for 1) status update notification event and 2) trace event based on status update from status aggregate service.
Database: stores 1) user login status per device, 2) raw traces
Cache: store previous calculated user status for perf.
Analytics service: Aggregate raw traces into metrics and stores in Analytics store.
Detailed Component Design
Notification service handles client registration/unregistration on a given user's status update. It will keep the connection alive with Auth, and broadcast a user's login status update to all subscribers. Each client will register with client id, so that multiple devices of a given subscriber will receive notification for the same person.
Once a user is login to the system (Login/out is out of scope of this system), status aggregate service will be called to put a status update event on the message queue. Then another handler in the status aggregate service will listen to the event, and do a database update for that given login from a specific device id. Once the database update is successful, it will push an aggregate status update event back to the message queue. Then another handler will pick up this event, try to find pre-existing login status for the given user from cache, if no cache hit, will query data from DB and refill cache. Once the aggregate table is updated, another event will be put on the message queue for sending notification to subscribers.
When there is concurrent status write, we will use the version of the object as a lock - when an update holds different version than the latest one, the update will fail. And the caller will retry with jitter. This will guarantee eventual consistency.
When status aggregate service received login/logout status update request, it will put a trace event to the message queue. Then Tracing service subscribe to the event will store the data in database.
Analytics service can be triggered by scheduler or trace recorded updated event from database to calculate metrics like sessions, active time, ...etc. The metrics then will be stored in Analytics store as a multi-dimensional database for easy slice and dice data.
Since each service is a microservice, and we leverage message queue for async process (e.g., trace, notification). It makes the service scalable.