Most chat application users expect delivery or read receipts on the messages they send. WhatsApp was the first to introduce this feature back in 2014, while almost all of the other chat apps in the market soon followed suit. With this design pattern, you’ll understand how to implement read receipts in your chat apps using Ably.
What are ‘read receipts’?
Continuing from WhatsApp’s example, we know that when we send a message via WhatsApp, we see a single grey tick if the message is sent successfully, double grey ticks if the message is received successfully by the recipient, and double blue ticks if the message is seen or read by the recipient.
This feature can be quite easily implemented using Ably. The implementation details are below.
Implementation details
Ably deals with messages in realtime using named channels. For implementing read/delivery receipts in a chat app, you’ll need to make use of these channels to send and receive both the actual message contents that are being exchanged by different parties participating in the chat app, as well as the messages corresponding to receipt data. Since all of this needs to be done in realtime, you’ll need to use Ably's realtime client library.
For the read receipts, you can use a separate channel altogether, so that the messaging business logic is left undisturbed. Of course, we also need to know the presence status of the users. So, for a generic chat app between two users, let us consider three channels as shown below. This is explained further.
Channels
Let’s name the three channels as below:
“messages-channel”
“presence-channel”
“read-receipts-channel”
Note that the arrows in the illustration above depict the direction of flow of data in these channels.
The messages channel can be used by the users to send and receive messages from other users. Hence, they need to have both publish and subscribe permissions on this channel. This messages channel can be set up to use the Ably Integrations service, so you get a buffer/ shock absorber to cope with a high volume of incoming messages. Ably Integrations is a scalable service that lets you implement load balancing on your servers, trigger events or serverless functions in realtime, and even stream data in realtime to other third-party streaming services.
Next, the presence channel can be used by the users to send their presence events to the server and also receive presence events from other users via the server. The users have both publish as well as subscribe rights on this channel.
The final one is the read-receipts channel. This channel is our main point of discussion throughout this Design Pattern. We’ll use this channel to send and receive various message delivery receipts i.e, ‘sent’, ‘delivered’, and ‘seen’. Everyone has publish as well as subscribe permissions to this channel.
Latencies are sorted but what about the order of messages?
Ably ensures that messages are delivered to persistently connected subscribers on a channel in the order they were published on that channel by any other Ably client when using one of the realtime libraries. Read more about it in this support article.
Caveats
Ably currently caps peak subscriptions to presence updates at 200, hence be mindful of that. If you don’t need your users to know the presence status of all the other users, this problem is completely solved. In that case, the presence channel can be used by the clients to send their presence events to the server. In turn, the server can subscribe to this channel to be notified of this info whenever a presence event occurs. Everyone will have presence permissions on the presence channel, but not subscribe permission. This means you have the ability to query the presence on a channel or subscribe to presence events from your server, but the clients themselves won’t be able to inspect/subscribe to this channel. This avoids the classic n-squared problem, e.g. 10k people entering and subscribing to 10k ‘presence-enter’ events would result in 100m messages. However, 10k people entering, generating 10k presence events only for your server results in just 20k messages.
You have a reliable way to consume data from clients with a shock absorber (the queue). You can also simplify how you process data, by using workers.
Assuming the number of clients for such an application is generally high, you can group them into clusters based on their geographical location. You would have the three channels for each cluster implemented, with the same functionality. You can ensure you have a constant amount of data published and received regardless of cluster size or activity. Clients won’t become “overloaded” with too much data, potentially that you don’t have control over.
Live demo
We have a live demo that is intended to demonstrate the design pattern described above. As such, it works best with at most two chat instances.
You can see the full code for the demo app in Glitch.
Recommended Articles
Live multiplayer quiz
Recommendations to build a scalable multiplayer quiz app with architecture comparisons along with a starter kit and a demo.
Apache Kafka and Ably: Building scalable, dependable chat applications
Learn how Ably complements and extends Kafka to end-users at the edge, enabling you to architect scalable, dependable chat apps.
Scaling Firebase - Practical considerations and limitations
Firebase is used to keep frontend clients and your backend in realtime sync - for example, chat apps, and multiplayer collaboration functionality. But how well does it scale?