History
Ably's message History feature doesn’t replace a long-term database, it’s for short-term catch-up, allowing clients to retrieve messages they missed during a brief disconnection. Using it as a primary database is an architectural anti-pattern that will lead to problems.
When a client disconnects, Ably automatically handles the reconnection. If the disconnection lasts less than 2 minutes, Ably's connection-state recovery feature streams any missed messages to the client. For longer outages, the client must use the History API to fetch the missed messages. The API works efficiently because it indexes all messages by channel, timestamp, and serial number, allowing retrieval from any point in time.
It's important to understand that History is not a database. The feature is purely for time-based retrieval on a single channel. It simply cannot support the complex queries that modern applications require. For example, the History API does not index message content or publisher IDs, so you cannot:
- Search messages by their author (clientId).
- Perform a full-text search on message content.
- Run relational queries, like grouping messages into threads.
For any application needing complex queries or long-term data persistence, you must use a dedicated database. The best practices for consuming Pub/Sub data and persisting it to the right storage solutions are covered in our integrations documentation.
You can also learn more about connection states and the broader platform architecture.
History versus rewind
You can retrieve previously published messages using the history feature or using the rewind channel option. Key differences between the two features:
- History can return up to 1000 messages in a single call, as a paginated list. Rewind returns at most 100 messages.
- The
history()method can be called repeatedly with different parameters. Rewind only has an effect on an initial channel attachment. - You can define a custom start and end time to retrieve messages from using history. Rewind returns either a given number of messages, or messages up to a point in time in the past.
- History is available when using the realtime and REST interfaces of an SDK. Rewind is only available using the realtime interface.
- Only history can return previously published presence events.
Retrieve channel history
The Ably SDKs provide a straightforward API to retrieve paginated message event history. By default each page of history contains up to 100 messages and is ordered from most recent to oldest. You can retrieve channel history by using the history() method.
Multi-channel history limitations
History is stored per channel. You cannot retrieve history from multiple channels in a single call. Each history request must target a specific channel.
To retrieve history from multiple channels, iterate over your channels and call the history method for each one. You can do this synchronously (one after another) or asynchronously (in parallel):
- Synchronous: Call history for each channel sequentially. Simpler but slower.
- Asynchronous: Make parallel history calls for better performance. Combine and sort results by timestamp if needed.
This pattern is commonly used when implementing channel sharding, where related data is distributed across multiple channels.
This example retrieves the latest message sent on a channel:
1
2
3
4
5
6
7
const realtime = new Ably.Realtime('demokey:*****');
const channel = realtime.channels.get('hay-pot-cam');
await channel.publish('example', 'message data');
const history = await channel.history();
const lastMessage = history.items[0];
console.log('Last message: ' + lastMessage.id + ' - ' + lastMessage.data);Channel history parameters
Query parameters for the options object when calling history(). Note that untilAttach is only available when using the realtime interface of an Ably SDK:
| Parameter | Description |
|---|---|
| start | earliest time in milliseconds since the epoch for any messages retrieved |
| end | latest time in milliseconds since the epoch for any messages retrieved |
| direction | forwards or backwards |
| limit | maximum number of messages to retrieve per page, up to 1,000 |
| untilAttach | when true, ensures message history is up until the point of the channel being attached. See continuous history for more info. Requires the direction to be backwards (the default). If the channel is not attached, or if direction is set to forwards, this option will result in an error. |
It is possible to use the history API to retrieve the last message published to a channel that has been persisted for up to a year with the persist-last feature, if enabled, even if there is no history available from normal persisted history (if there have been no messages published on the channel for longer than the history retention period). To do this, make a history query with limit=1 and no start or end time.
Continuous history
By using rewind or history's untilAttach, it is possible to obtain message history that is continuous with the realtime messages received on an attached channel. For example, a user joining a navigation app with traffic updates would receive the latest traffic update and be subscribed to any new traffic updates.
Rewind
If you wish to obtain history as part of attaching to a channel, you can use the rewind channel parameter. This will act as though you had attached to a channel from a certain message or time in the past, and play through all messages since that point. Rewind can only be used when first attaching to a channel.
A rewind value that is a number (n) is a request to attach to the channel at a position of n messages before the present position. rewind can also be a time interval, specifying a number of seconds (15s) or minutes (1m) to replay messages from.
Note that this is only available with the realtime interface.
This example subscribes to the channel and relays the last 3 messages:
1
2
3
4
5
6
7
const realtime = new Ably.Realtime('demokey:*****');
const channel = realtime.channels.get('hay-pot-cam', {
params: {rewind: '3'}
})
await channel.subscribe((message) => {
console.log('Received message: ', message)
});You can also qualify a channel name with rewind when using the service without a library, such as with SSE or MQTT.
History with untilAttach
You can obtain message history that is continuous with the realtime messages received on an attached channel, in the backwards direction from the point of attachment. When a channel instance is attached, it's automatically populated by the Ably service with the serial number of the last published message on the channel. As such the serial number can be used to make a history request to the Ably service for all messages published before the channel was attached. Any new messages therefore are received in real time via the attached channel, and any historical messages are accessible via the history method.
Use the untilAttach option when making history requests on attached channels. If the channel is not yet attached, this will result in an error.
1
2
3
4
5
6
7
8
const realtime = new Ably.Realtime('demokey:*****');
const channel = realtime.channels.get('hay-pot-cam');
await channel.publish('example', 'message data');
await channel.attach();
const history = await channel.history({untilAttach: true});
const lastMessage = history.items[0];
console.log('Last message before attach: ' + lastMessage.data);Retrieve presence history
Retrieve presence history using the history() method on the presence object. This enables a client to retrieve historical presence events from the channel.
This example retrieves a paginated list of historical presence events published:
1
2
3
4
5
6
7
8
await channel.presence.enter('enter');
const history = await channel.presence.history();
console.log(history.items.length + ' presence events received in first page');
if (history.hasNext()) {
const nextHistory = await history.next();
console.log(nextHistory.items.length);
}Presence history parameters
Query parameters for the options object when calling presence.history():
| Parameter | Description |
|---|---|
| start | earliest time in milliseconds since the epoch for any messages retrieved |
| end | latest time in milliseconds since the epoch for any messages retrieved |
| direction | forwards or backwards |
| limit | maximum number of messages to retrieve per page, up to 1,000 |
Global history synchronization
Channel history is synchronized across all datacenters within approximately 100 milliseconds of message publication. This ensures consistent history retrieval regardless of which datacenter serves the request.
Synchronization process
When a message is published, it moves through a multi-stage synchronization process.
When a message is published:
- The message is immediately available in ephemeral storage (Redis) at the publishing datacenter.
- Within 100ms, the message propagates to persistent storage across multiple datacenters.
- History requests from any region will return the same message set once synchronization completes.
Storage consistency
Different storage types have different consistency guarantees.
The following table explains storage consistency levels:
| Storage type | Synchronization time | Consistency level |
|---|---|---|
| Ephemeral storage | Immediate at publishing datacenter | Eventually consistent globally |
| Persistent storage | ~100ms across all datacenters | Strong consistency via quorum |
| Message survivability | 99.999999% after acknowledgment | Replicated to 3+ regions |
Implications for applications
Understanding these synchronization characteristics helps you design robust applications.
- History requests may show slight delays (up to 100ms) for messages published in remote datacenters.
- Connection recovery works reliably regardless of datacenter failover.
- Message continuity is preserved even during regional failures.
- Applications should not depend on instant global history consistency for real-time features.
Ordering of historical messages
The order in which historical messages are returned with history is based on the message timestamp that was assigned by the channel in the region that the message was published in. This ordering is what Ably calls the canonical global order.
This is not necessarily the order that messages were received by a realtime client. The order in which each realtime client receives a message depends on which region the client is in.
Ably preserves ordering for a specific publisher on a specific channel but, for example, if two publishers in regions A and B publish message-one and message-two simultaneously, then it is very possible that a subscriber in region A will receive message-one before message-two, but that a subscriber in region B will receive message-two before message-one.
There are some instances where messages will not be in canonical global order when using continuous history. Continuous history utilizes either a REST history call with the untilAttach parameter set to true, or the rewind feature on a realtime connection. Messages that are less than two minutes old are retrieved from live ephemeral storage and are ordered only by region. This is to ensure that they correctly mesh with the live stream of messages currently being published in that region. Messages older than two minutes are retrieved from persisted history (if it is enabled). These messages will be in canonical global order. Ably ensures that the transition between those messages retrieved from ephemeral storage, and those from persisted storage, occurs without duplication or missed messages.