I have recently seen a system design for bidding service and one of its use cases was real-time delivery of bid updates for a specific item (or a set of items) to the client.
The design was quite abstract and did not elaborate on specific details of individual components. Hence I am trying to figure out how it could be implemented technically.
The design suggests the following:
- bids are sent asynchronously from backend service called
bids serviceto event queue (the design does not elaborate which exactly queue service it is, it only mentions that this is an event queue) - there is a backend service called
real-time servicewhich consumes this event queue and sends real-time updates to clients through websocket connection
Below is my rough understanding of how it could be implemented:
Let's assume that "real-time service" has 2 instances (instance1, instance2).
Suppose that user1 is connected to instance1 and user2 is connected to instance2 through websocket. Both subscribe to item5.
if AWS SQS is used as a queue, then all instances will listen to it, but every event will be consumed and ACKed by only one of those instances and then removed from the queue, which means that the other instances won't receive it and hence won't be able to send status updates to their connected users.
Example: When event foritem5becomes available in the queue, it is consumed byinstance2and sent touser2. Butuser1will not get that event becauseinstance1did not consume it.if AWS Kinesis is used as a queue, then the above problem disappears because Kinesis persists the data for a specific retention window. So both instances will consume the event and send it to
user1anduser2. But the negative side here is that every instance will be processing every incoming Kinesis event, regardless of whether connected users actually need it or not.
Example: Assume there are lots of users connected to the instance and all are subscribed toitem5. But there are only events foritem3in the queue. Nevertheless, for each consumed event, the service will need to check client subscriptions to see if any of them needsitem3and then just discard it because no one needs it.if pubsub is used like e.g. Kafka or RabbitMQ (with topic name identical to
itemID) then things get way better compared to Kinesis because instance can subscribe to specific topics only, ignoring everything else, and when an event gets consumed from one of topics, the service checks which clients have been subscribed to the event, and sends this event to all those clients.
In the reasoning above, I also assume that every service instance maintains an in-memory mapping of itemId-to-<list of clientIds> subscriptions, in order to be able to quickly find a list of clients waiting for update from a specific item.
For example, if client1 is connected and waits for item1 and item2, and client2 waits for item2 and item3 on the same instance of real-time service, then the map will look like:
item1 -> {client1}
item2 -> {client1, client2}
item3 -> {client2}
Does all my reasoning make sense here? Is this how real-time streaming actually implemented?
Note that this is applicable not just for bidding system but also for instant messaging, where the clients could subscribe to other clients or groups (and real-time service is actually a chat service)
PS. I never used a queue/pubsub subscriber as part of a web backend application. Is that a good practice to do so?