Realtime has two distinct message delivery modes: "at-most-once" and "at-least-once" (aka guaranteed delivery mode).
A messaging system implements an "at-most-once" delivery mode (or contract) when the only guarantee is that each message will be delivered only one time to each subscriber, if it gets delivered at all.
The first part is great, "only one delivery", but the second part not so much, "if delivered at all". This basically means that messages can be lost when an "at-most-once" contract is in place.
This mode is good for apps where messages contain full data updates sent frequently (not incremental updates), like dashboards or stock tickers, where only the last message is important since it holds the last known "truth". In this scenario if a message is lost the next one will contain the necessary data to correctly update the app's state and/or user interface.
However, if each message contains only a small incremental update to the app state, like in a chat app, the "at-most-once" guarantee will become insufficient (the chat can become out-of-sync) and will force developers to "hack" some workarounds, like frequently syncing the chats querying the app backend or more optimally, syncing when the app detects a Realtime reconnection.
The send, subscribe and subscribeWithFilter methods use the "at-most-once" delivery mode.
The "at-least-once" mode offers a stronger guarantee: all messages will be delivered at least once. This means each chat participant will always receive each chat message, independently of it's connection state at the moment the message was sent. Frequent re-syncing "hacks" are no longer required.
Why "at-least-once" and not "exactly-once"?
Most times the "at-least-once" mode will in fact be "exactly-once and in order", but under some abnormal circumstances (like severe network failures) a subscriber can receive the same message more than once.
Your app should be aware of this and detect these unfrequent (but possible) duplications. Hopefully, as we'll explain below, each message has a unique identifier that you can use to easily identify duplications.
The Realtime Platform knows that a subscriber has successfully received a message when the subscriber sends an acknowledge (this happens automatically inside the Realtime SDK).
If there's a severe network failure at the moment this acknowledge is being sent, the Realtime Platform may not receive the acknowledge in due time and will consider that message was lost, adding the message to a "virtual" subscriber buffer.
This buffer will be flushed and the message sent again as soon as possible, possibly causing a duplication.
The two methods allowing the usage of the "at-least-once" delivery mode are subscribeWithBuffer and publish.
The subscribeWithBuffer method will establish a channel buffered subscription uniquely identified by a subscriberId parameter.
Through the subscriberId the platform will know exactly which was the last message successfully delivered to that specific subscriber.
To avoid "strange" behaviours in your app you should guarantee that each subscriber will always use the same unique subscriberId throughout the app lifecycle. Your app's internal userId is a good option since it will never change during the user "lifetime".
On the sender side you must use the publish method. This method adds the message to the channel sequence log, giving it an unique sequenceId and finally routes the message to the subscribers.
The sequenceId will never be repeated for any other message in the future and it should be used to detect message duplication.
Most times the messages sent with publish will be delivered in real-time, only a few milliseconds after publishing, but in some extraordinary cases, like a reconnect, the non-delivered (buffered) messages will be delivered later and in strict order, when the subscriber is ready to receive them (we call this process a subscriber re-sync).
To sum it up, whenever you send a message with the publish method, you can rest assured that message will be delivered in order to each subscriber
that used the subscribeWithBuffer method.
As simple as that!
Another cool feature of the publish method is the message time-to-live (TTL) parameter. This will allow automatic non-delivered message expiration from any subscriber buffer.
The minimum expiration interval is 1 second and the maximum will vary according to the Realtime plan being used by the app (2 minutes on the Free plan and 72 hours on paid plans).
You'll also be able to subscribe a channel using a buffered subscription using the subscribeWithOptions method. This method allows you to mix different subscription types, like filters and push notifications. This means that you can easily subscribe a channel with a filter and also with a buffer for guaranteed delivery. Please refer to the specific SDK reference documentation for more details.
The first limitation is related to the time-to-live (TTL) maximum value. It's 2 minutes on the Free plan and 72 hours on the paid plans.
Another global limitation is related to publish frequency. The Realtime Platform will enforce a 10 messages per second rate limit for each client connection. It will allow some bursts above that rate limit but messages will be buffered and sent with a 10 messages per second throttling. If the pending messages buffer exceeds 100 messages, newly published messages will be discarded and a client exception will be throw'ed (you can easily handle this exception and resend the message later when the buffer is flushed).
If you need to publish messages with a higher frequency you must use several client connections to distribute message publishing or the use send method instead (without delivery guarantee).
Both delivery modes share the same pricing with one exception: every message delivered to a subscriber from the message buffer (not delivered in real-time but during a subscriber re-sync procedure) will count as two (2) messages towards the monthly usage.
Currently only the following Realtime SDKs implement the subscribeWithBuffer and publish methods (please let us know if the SDK you need is not yet in this list):
Now it's time to see the "at-least-once" mode in action.
The following demo uses the "at-least-once" delivery mode in a simple chat: http://demos.realtime.co/demos/bufferedchat.aspx