Channels
Channels are used to separate messages into different topics. They provide a way to implement the Publish-Subscribe (Pub/Sub) architectural pattern. Pub/Sub enables any number of publishers to publish messages to a channel, and any number of subscribers to be subscribed to a channel to receive them, with publishers and subscribers completely decoupled from one another.
Clients can subscribe to channels and publish messages using the realtime interface of an Ably SDK. Clients can only publish messages when using the REST interface.
Channel namespaces
Channel namespaces enable channels to be grouped together based on a prefix that is included as part of the channel name. A colon :
is used to delimit a channel namespace, and a namespace is the first segment of a channel name up until the first colon. If a channel name does not contain a colon, the namespace is the entire channel name.
The following are examples of channels that are all part of the ‘customer’ namespace:
customer
customer:tracking-id
customer:order:update
Namespaces can be used to apply operations to all channels within the namespace, such as capabilities, channel rules and integration rules. Namespaces are not required to refer to a set of channels within a capability. A resource specifier, such as foo:*
, a glob expression, will match a channel named foo:bar
, even without a foo
namespace.
Create or retrieve a channel
A Channel
object is a reference to a single channel and is uniquely identified by its unicode string name. A channel is created, or an existing channel is retrieved from the Channels
collection, using the get()
method. You can only connect to one channel in a single operation. Wildcards are not supported.
Although Ably recommends that you use channels to distribute work more evenly across the cluster, there is an associated cost for a high number of channels. Don’t use different channels just to indicate different types of data, or different events, if all messages are going to the same set of clients. Use a single channel and distinguish between them using a different message name
.
Channels are the unit of security and scalability. If you are sending data that must not be shared with certain clients, ensure it is on a channel that those clients don’t have the capabilities to attach to.
The following is an example of creating a channel:
const channel = realtime.channels.get('channelName');
CopyCopied!
Attach to a channel
Attaching to a channel ensures that it is created in the Ably system and that all messages published on the channel are received by any channel listeners registered by calling subscribe(). Ably will start to stream messages to a client as soon as they have attached, regardless of whether or not they have yet subscribed. Attach is only available to the realtime interface.
Channels are not pre-configured or provisioned by Ably in advance. They are created on demand when clients attach, and remain active until such time that there are no remaining attached clients.
Note that attach()
can be called explicitly, however it’s more common for a client to subscribe, which will automatically initiate the attach.
The following example explicitly attaches to a channel, which results in the channel being provisioned in Ably’s global realtime cluster. This channel will remain available globally until there are no more clients attached to the channel:
const channel = realtime.channels.get('chatroom');
await channel.attach();
CopyCopied!
Detach from a channel
A client can detach from a channel so that it no longer receives any messages published to that channel. Detaching is different to unsubscribing from a channel because unsubscribe()
is a client-side operation. The Ably platform does not know that a client has unsubscribed and will continue to stream messages to that client until detach()
is called.
A channel will automatically close when all of the following criteria are met:
- There are no more realtime clients attached to it
- Approximately one minute has passed since the last client detached
- Approximately one minute has passed since the last message was published to the channel
The following is an example of detaching from a channel:
const channel = realtime.channels.get('chatroom');
await channel.detach();
CopyCopied!
Publish a message
Use the publish()
method to send messages to a channel. All clients that are subscribed to that channel will receive the messages. Publishing messages is an operation available to the REST and realtime interfaces.
The following is an example of publishing a message to a channel:
const realtime = new Ably.Realtime('<loading API key, please wait>');
const channel = realtime.channels.get('hue-jug-tab');
await channel.publish('example', 'message data');
Demo OnlyCopyCopied!
To publish a single message to multiple channels, make multiple publish()
requests using the realtime interface. These concurrent requests can be in-flight simultaneously, ensuring that a publish on one channel does not delay operations in other channels. To publish to multiple channels use the batch publish feature.
Batch publish
It is possible to publish messages to multiple channels with a single request. A batch request queries an API multiple times with single HTTP request. A batch request has a single set of request details containing the request body, parameters and headers. These are converted into an array of requests to the underlying API. Each individual request to the underlying API is performed in parallel and may succeed or fail independently.
The following is an example of a batch publish request using the request()
method to query the batch REST API
const ablyRest = new Ably.Rest({ key: '<loading API key, please wait>' })
const content = { 'channels': [ 'test1', 'test2' ], 'messages': { 'data': 'myData' } }
const batchPublish = await ablyRest.request('post', '/messages', null, content, null);
console.log('Success! status code was ' + batchPublish.statusCode)
Demo OnlyCopyCopied!
Batch requests
Each batch publish request can contain a single BatchSpec
object, or an array of BatchSpec
objects. Each BatchSpec
object contains a single channel name or an array of channel names in the channels
property. The messages
property then contains a single message or an array of messages. Each BatchSpec
will then publish each of its messages to each of its channels.
For each channel, the messages grouped into a single BatchSpec
are published atomically. This means that:
- Either they will all be successfully published or none of them will
- The max message size limit applies to the total size of all messages in in a
BatchSpec
- Each
BatchSpec
will only count as a single message for the purpose of the per-channel rate limit
So if you do not need the atomicity guarantee and might be in danger of exceeding the max message size limit, you can put each message into its own BatchSpec
(relative ordering will still be preserved). Conversely, if you are publishing many hundreds of small messages and are in danger of exceeding the max per-channel message rate, you group them into a fewer BatchSpecs
.
The batch request as a whole is subject to the following limits:
- Each request can only include 100 different channels. If the same channel name appears in multiple
BatchSpec
objects within a single request, it only counts as one channel towards the 100 channel limit per batch request. - Each request has a maximum body size of 2MiB.
The following is an example of a single BatchSpec
object publishing a single message to 2 channels:
{
channels: ['channel1', 'channel2'],
messages: {data: 'My message contents'}
}
CopyCopied!
The following is an example of an array of BatchSpec
objects. The first publishes a single message to two channels and the second publishes two messages to a single channel:
[
{
channels: ['channel1', 'channel2'],
messages: {data: 'My message contents'}
},
{
channels: 'channel3',
messages: [
{data: 'My message contents'},
{name: 'an event', data: 'My event message contents'},
]
}
]
CopyCopied!
The following is an example curl request, querying the REST API directly:
curl -X POST https://rest.ably.io/messages \
-u "<loading API key, please wait>" \
-H "Content-Type: application/json" \
--data '{ "channels": [ "test1", "test2"],
"messages": {"data": "My test message text" } }'
Demo OnlyCopyCopied!
Batch responses
Once all requests have been completed in a batch request, a batch response is returned with three possible outcomes:
- Success
- If all of the individual requests were successful then an array containing the response of each query is returned in request order.
- Failure
- If the batch request itself failed before the individual requests were made, then an error response is returned with a status code and error response body. Examples of why the batch request can fail include an authorization failure or an invalid request.
- Partial success
- If one or more of the individual requests failed the response body contains an error object with the error code
40020
and a status code of400
. The error body contains abatchResponse
array of each individual response in request order. ThebatchResponse
can be inspected if there is a need to know the details of each outcome. If you only need to know whether or not the batch request was completely successful then the status code is sufficient.
The examples for each possible outcome will use the following BatchSpec
object as the request data:
{
channels: ['channel0', 'channel1', 'channel2'],
messages: {data: 'My test message text'}
}
CopyCopied!
The following is an example of a successful batch publish response. The response body contains the messageId
of each published message and the channel
it was published to. The status code is 201
:
[
{
"channel":"channel0",
"messageId":"w234r5t-fr5"
},
{
"channel":"channel1",
"messageId":"vde4sfc0p"
},
{
"channel":"channel2",
"messageId":"nh3exv8ih"
}
]
CopyCopied!
The following is an example of a batch publish failure response. The response body contains the details of the error
, in this example that the token used for the request has expired. The status code is 401
:
{
"error": {
"message":"Token expired",
"statusCode":401,
"code":40140
}
}
CopyCopied!
The following is an example of a batch publish partial success response. The successful requests contain the messageId
of each published message and the channel
they were published to. The failed request contains the channel
the request failed for and the details of the error
, in this example that the credentials used didn’t have the capability to publish to that channel. The status code for a partial success is always 400
:
{
"error": {
"message": "Batched response includes errors",
"statusCode":400,
"code":40020
}
"batchResponse": [
{
"channel":"channel0",
"messageId":"w234r5t-fr5"
},
{
"channel":"channel1",
"messageId":"vde4sfc0p"
},
{
"channel":"channel2",
"error": {
"message": "Given credentials do not have the required capability",
"statusCode": 401,
"code": 40160
}
}
]
}
CopyCopied!
Transient publish
Transient publishing is when a client publishes messages without attaching to a channel. This is a feature of the realtime interface of certain Ably SDKs. Transient publishing can be beneficial if you intend to publish to many channels as it removes the need to attach to a channel each time you publish. It also avoids a client subscribing to messages which avoids messages being sent to it redundantly.
The following is an example of publishing without attaching to a channel:
const channel = realtime.channels.get('chatroom');
// The publish below will not attach you to the channel
await channel.publish('action', 'boom!');
CopyCopied!
Idempotent publish
Idempotency ensures that multiple publishes of the same message cannot result in duplicate messages.
It is possible that a client publishing a message using the REST interface may not receive acknowledgement of receipt from Ably, due to issues such as network failure outside of Ably’s control. Clients will internally attempt to re-publish messages in these instances.
When idempotent publishing is enabled, the Ably SDK will internally assign a unique ID to each message which ensures that subsequent retry attempts cannot result in duplicate messages. Idempotent publishing is enabled by default in all latest Ably SDKs. It can be disabled by setting the idempotentRestPublishing
ClientOptions
to false
.
Note that Ably can only detect duplicate messages within a 2-minute window after the original message, with the same ID, is published. If a message with the same ID is published after this 2-minute window, it will be treated as a new message.
You can also specify message IDs externally. The following is an example of how you might do this:
const rest = new Ably.Rest('<loading API key, please wait>');
const channel = rest.channels.get('hue-jug-tab');
await channel.publish([{data: 'payload', id: 'unique123'}]);
Demo OnlyCopyCopied!
If manually specifying message IDs, it is important to be aware of how messages are published when calling the publish() method with an array of messages. See this FAQ for further information.
Use the REST interface to publish on behalf of a realtime connection
You can use the REST interface of an Ably SDK to publish messages on behalf of a realtime connection.
To publish on behalf of a realtime connection, the REST publisher requires the connectionKey
of the realtime client. The connectionKey
is a secret of the client unless explicitly shared. The REST publisher can then set the connectionKey
in the root of the published message.
If the realtime connection is identified by being bound to a clientId
, then the REST publish must include that same clientId
. This can be included in the message itself to apply to only that message, in the case that the REST client is able to assume any clientId
, or using a REST client bound to that specific clientId
.
The publish attempt will fail in the following scenarios:
- the
connectionKey
is invalid - the
connectionKey
belongs to a connection that has since been closed - the REST publisher is using a different Ably application to the realtime client
- the
clientId
s don’t match between the realtime connection and the REST publish
Subscribe to a channel
Subscribe to a channel in order to receive messages being published to it, by registering a listener. Subscribing is an operation available to the realtime interface and uses the subscribe()
method.
Subscribing to events server-side using the Pub/Sub method can be disadvantageous as it can increase latency or duplicate events among multiple servers. Message Queues are more a appropriate method to use in that instance, as multiple worker servers enable Ably to distribute the load of messages received from published. This means that each message is only processed once by any one of your worker servers.
A client can subscribe to all messages published to a channel by passing a listener function to the subscribe()
method. The listener is passed a Message
object for each message received. Alternatively, a client can listen for a subset of messages based on the name of the published message.
The following is an example of registering a listener for all messages:
const realtime = new Ably.Realtime('<loading API key, please wait>');
const channel = realtime.channels.get('hue-jug-tab');
await channel.subscribe((message) => {
alert('Received: ' + message.data);
});
Demo OnlyCopyCopied!
The following is an example of registering a listener for a specific message name:
await channel.subscribe('myEvent', (message) => {
console.log('message received for event ' + message.name);
console.log('message data:' + message.data);
});
CopyCopied!
Although the attach operation can be initiated explicitly by a client, it is more common for the client to simply subscribe, which will automatically initiate the attach, if the channel is not already attached.
Normally, errors in attaching to a channel are communicated through the attach() callback. For implicit attaches there is no callback, so if you want to know what happens, you’ll need to listen for channel state changes. This is also true in other cases where a channel is attached or re-attached automatically, for example, following the library reconnecting after a period in the suspended
state.
The following is an example of implicitly attaching to a channel and publishing a message:
const channel = realtime.channels.get('chatroom');
await channel.subscribe('action', (message) => { // implicit attach
console.log('Message received ' + message.data);
});
await channel.publish('action', 'boom!');
CopyCopied!
Unsubscribe from a channel
Unsubscribing from a channel removes previously registered listeners that were added when subscribing to it.
The following is an example of removing listeners registered for a single event and an example of removing listeners registered for all events:
/* remove the listener registered for a single event */
channel.unsubscribe('myEvent', myListener);
/* remove the listener registered for all events */
channel.unsubscribe(myListener);
CopyCopied!
Subscription filters
Subscription filters enable you to subscribe to a channel and only receive messages that satisfy a filter expression.
Messages are immediately streamed to clients as soon as they attach if they have subscribe capabilities for that channel. Subscription filters apply server-side filtering to messages, meaning that a client will only ever receive the messages that they subscribe to.
Subscription filters are currently in preview status.
Create a filter expression
Filter expressions should be written using JMESPath. They can be constructed using the message name and message.extras.headers
fields.
message.extras.headers
optionally provides ancillary metadata to a message, as Ably can’t inspect message payloads themselves. Adding suitable key-value pairs to messages will enable more complicated filter expressions to be constructed resulting in more effective message filtering.
The following is an example of publishing a message with additional metadata:
const channel = realtime.channels.get('scoops-kiosk');
await channel.publish({
name: 'ice-cream',
data: '...',
extras: {
headers: {
flavor: 'strawberry',
cost: 35,
temp: 3
}
}
});
CopyCopied!
Be aware that message.extras.headers
must be a flat object. It can’t contain any further nesting or arrays.
The following is an example of a filter expression subscribing to messages with the name “ice-cream”, a flavor of “strawberry” and a cost of less than 50:
name == `"ice-cream"` && headers.flavor == `"strawberry"` && headers.cost < `50`
CopyCopied!
The following is an example of a filter expression subscribing to messages with a flavor of either “strawberry” or “chocolate”:
headers.flavor == `"strawberry"` || headers.flavor == `"chocolate"`
CopyCopied!
Subscribe with a filter
In order to subscribe to a channel with a filter expression, you obtain a channel instance using the getDerived()
method. This accepts a filter expression as a parameter.
The following is an example of subscribing to a channel using one of the previous example filters:
const channel = realtime.channels.getDerived('scoops-kiosk', {
filter: 'name == `"ice-cream"` && headers.flavor == `"strawberry"` && headers.cost < `50`'
})
await channel.subscribe(...);
CopyCopied!
The following example demonstrates publishing to a channel, but subscribing to only a subset of messages on it:
// Connect to Ably
const realtime = new Ably.Realtime({'<loading API key, please wait>'});
// Create a channel instance to publish to
const pubChannel = realtime.channels.get('scoops-kiosk');
// Create a channel instance using the filter qualifier
const subChannel = realtime.channels.getDerived('scoops-kiosk', {
filter: 'name == `"ice-cream"` && headers.flavor == `"strawberry"` && headers.cost < `50`'
});
// Subscribe to the channel using the filtered subscription
await subChannel.subscribe((message) => {
alert('Ice cream update: ' + message.data);
});
// Publish to the unfiltered channel instance
await pubChannel.publish({
name: 'ice-cream',
data: '...',
extras: {
headers: {
flavor: 'strawberry',
cost: 35,
temp: 3
}
});
});
Demo OnlyCopyCopied!
Subscription filter capabilities
Clients require the subscribe capability for one of the following resources in order to receive messages from a subscription filter:
[filter]<channel name>
[*]<channel name>
[*]*
A client may also attach to the unfiltered instance of a channel for other operations, such as to subscribe to the presence set. Be aware that if clients attach to the unfiltered instance, and have the subscribe capability for the channel itself, they will be sent all messages by Ably. This is because of the difference between attaching and subscribing to a channel.
Subscription filter limitations
The following features are not supported using subscription filters:
Server-side batching
Server-side batching optimizes message processing and delivery by grouping multiple messages into batches before sending them to subscribers. This feature reduces the overall message count, lowers costs, and mitigates the risk of hitting rate limits during high-throughput scenarios.
Server-side batching is currently in beta status.
Key benefits
- Cost efficiency.
- Message rate limit mitigation.
Use cases
The following use cases explain the benefits of server-side batching:
- High-volume chat applications
- Chat platforms experience large spikes in message volumes during live events like sports games or concerts. This scenario increases costs and risks hitting message rate limits.
- Server-side batching groups multiple chat messages and reactions into single batches before delivery, reducing the number of individual messages processed and maintaining a seamless user experience.
- Fan engagement platforms
- Sports fan apps see surges in reactions during key moments, like goals, causing spikes in message volume.
- Server-side batching combines multiple reactions into batches before sending them, managing high volumes efficiently and preventing rate limits from being exceeded.
How server-side batching works
The server-side batching process works through a batching policy and a batching interval:
Batching policy
The batching policy defines the rules and behaviors for aggregating messages during the batching interval. The supported simple policy groups multiple incoming messages into a single batch without altering their content or metadata.
A batch can contain up to 200 messages by count or total data size. For example, if you have 210 messages, they will be split into two batches: one with 200 messages and another with 10 messages. If the combined data size of 200 messages exceeds the batch data limit, the excess bytes will be allocated to a new batch as separate messages.
Batching interval
The batching interval is a configurable period specified in milliseconds ranging from 20ms to 1s, during which incoming messages are collected and grouped into a batch. Messages sent to the server during this interval are temporarily held and aggregated. Once the interval elapses, the collected messages are combined into a single batch and delivered to subscribers as one message.
Choose the right interval for your app
- Longer intervals are preferred for cost efficiency.
- Align the interval with the expected user experience.
Configure server-side batching
The following steps take you through the server-side configuration process:
- On your dashboard, select one of your apps.
- Go to settings.
- Under channel rules, add a new rule.
- Enter the namespace or channel ID to which you want to add a new rule.
- Select server-side batching enabled.
- Choose a batching policy (only simple is supported in beta).
- Choose an interval range value from 20ms to 1s.
- Click create channel rule to save.
Configuration defaults
The following defaults apply when no configuration settings are specified:
- If no batching policy is specified, simple is used.
- If no interval value is specified, the default is 20ms.
Channel options
Channel options can be used to customize the functionality of channels. This includes enabling features such as encryption and deltas, or for a client to retrieve messages published prior to it attaching to a channel using rewind.
Channel metadata
Metadata provides additional information about apps or channels. It includes uses such as enabling clients to be aware of how many other clients are attached to a channel without the need to use the presence feature. Examples of channel metadata available include the status and occupancy of specific channels.
Channel rules
Channel rules can be used to enforce settings for specific channels, or channel namespaces. They can be broadly categorized into three different types:
- For message storage
- For client security and identification
- To enable features for a channel or namespace
The channel rules related to message storage are:
- Persist last message
- if enabled, the very last message published on a channel will be stored for a year. This message is retrievable using rewind by attaching to the channel with
rewind=1
. If you send multiple messages in a single protocol message, for example callingpublish()
with an array of messages, you would receive all of them as one message. Be aware that presence messages are not stored and that messages stored in this manner are not accessible using history. Note that for each message stored using this rule, an additional message is deducted from your monthly allocation. - Persist all messages
- if enabled, all messages published on a channel will be stored according to the storage rules for your account. This is 24 hours for free accounts and 72 hours for paid accounts. Messages stored in this manner are accessible using history. Note that for each message stored using this rule, an additional message is deducted from your monthly allocation.
- Server-side batching
- if enabled, messages are grouped into batches before being sent to subscribers. Server-side batching reduces the overall message count, lowers costs, and mitigates the risk of hitting rate limits during high-throughput scenarios.
The channel rules related to security and client identity are:
- Identified
- if enabled, clients will not be permitted to use (including to attach, publish, or subscribe) matching channels unless they are identified (they have an assigned client ID). Anonymous clients are not permitted to join these channels. Find out more about authenticated and identified clients.
- TLS only
- if enabled, only clients who have connected to Ably over TLS will be allowed to use matching channels. By default all of Ably’s client libraries use TLS when communicating with Ably over REST or when using our Realtime transports such as Websockets.
The channel rules related to enabling features are:
- Push notifications enabled
- If checked, publishing messages with a push payload in the
extras
field is permitted. This triggers the delivery of a Push Notification to devices registered for push on the channel. - Message interactions enabled
- If enabled, messages received on a channel will contain a unique
timeserial
that can be referenced by later messages for use with message interactions.
To set a channel rule in the Ably dashboard:
- Sign in to your Ably account.
- Select an app.
- Go to Settings tab.
- Click Add new rule.
- Select channel name or namespace to apply rules to.
- Check required rules.
Channel history
Channel history enables clients to retrieve messages that have been previously published on the channel. Messages can be retrieved from history for up to 72 hours in the past, depending on the persistence configured for the channel.
Presence
The presence feature enables clients to be aware of other clients that are ‘present’ on the channel. Client status is updated as they enter or leave the presence set. Clients can also provide an optional payload describing their status or attributes, and trigger an update event at any time.
Channel states
A channel can exist in any of the following states:
- initialized
- The
Channel
has been initialized, but no attach has been attempted yet. - attaching
- An attach has been initiated by sending a request to Ably. This is a transient state and will be followed either by a transition to attached, suspended, or failed.
- attached
- An attach has succeeded. In the attached state a client can publish and subscribe to messages, and enter the presence set.
- detaching
- A detach has been initiated on the attached
Channel
by sending a request to Ably. This is a transient state and will be followed either by a transition to detached or failed. - detached
- The
Channel
, having previously been attached, has been detached by the client. - suspended
- The
Channel
, having previously been attached, has lost continuity. This is normally due to the client being disconnected from Ably for more than two minutes. The client will automatically attempt to reattach as soon as connectivity is restored. - failed
- An indefinite failure condition. This state is entered if a
Channel
error has been received from the Ably service (such as an attempt to attach without the necessary access rights).
Channel state and connection state
Connection state also impacts the state of a channel in the following ways:
- If the connection state becomes
CLOSED
, all channels will becomeDETACHED
- If the connection state becomes
FAILED
, all channels will becomeFAILED
- If the connection state becomes
SUSPENDED
, all previously-ATTACHED
orATTACHING
channels will becomeSUSPENDED
- If the connection state becomes
CONNECTED
, any channels that wereSUSPENDED
will be automatically reattached
Listen for state changes
The Channel
object is an EventEmitter
. Events are emitted with a name
that corresponds to the new channel state, whenever there is a channel state change. Register a channel state change listener with the on()
or once()
methods, depending on whether you want to monitor all channel state changes, or only the first occurrence of one.
Remove channel state listeners with the off()
method.
Listeners are passed a ChannelStateChange object in the first argument. This object has the following properties:
current
/previous
: the present and last state of the channel.resumed
: a flag indicating whether message continuity on the channel is preserved since the last time the channel was attached.reason
: the reason for the state change, if available.
As with all events from an EventEmitter
in the Ably library, this
within the listener function is a reference to an event object whose event
property is the name of the event that fired. This allows a listener to listen for all events with a single registration and still know which type of event is fired.
The Channel
object can also emit one event that is not a state change: an update
event. This happens when there’s a change to channel conditions for which the channel state doesn’t change. For example, a partial loss of message continuity on a channel (typically after a resume) for which the channel state remains attached
would lead to an update
event being emitted, with both current
and previous
set to attached
and the resumed
flag set to false
. So if you get such an event, you’ll know there may be messages you’ve missed on the channel, and if necessary you can use history to retrieve them.
channel.on('attached', (stateChange) => {
console.log('channel ' + channel.name + ' is now attached');
console.log('Message continuity on this channel ' + \
(stateChange.resumed ? 'was' : 'was not') + ' preserved');
});
CopyCopied!
Alternatively, a listener may be registered so that it receives all state change events.
const myListener = (stateChange) => {
console.log('channel state is ' + stateChange.current);
console.log('previous state was ' + stateChange.previous);
if (stateChange.reason) {
console.log('the reason for the state change was: ' + stateChange.reason.toString());
}
});
channel.on(myListener);
CopyCopied!
Previously registered listeners can be removed individually or all together.
/* remove the listener registered for a single event */
channel.off('attached', myListener);
/* remove the listener registered for all events */
channel.off(myListener);
CopyCopied!
Be aware that when registering listeners for channel state changes, certain repeating states may add new listeners each time.
Handle channel failures
Channel attach and detach operations are asynchronous. After initiating an attach request, the client will wait for a response from Ably that confirms that the channel is established on the service and then trigger a state change event.
Ably SDKs will attempt to automatically recover from non-fatal error conditions. However, you can handle them yourself if you prefer by subscribing to channel state changes, or using the callbacks available when explicitly calling attach()
.
const channel = realtime.channels.get('private:chatroom');
channel.on('failed', (stateChange) => {
console.log('Channel failed, reason: ', stateChange.reason);
});
await channel.attach();
CopyCopied!
Fatal channel errors
Some classes of errors are fatal. These cause the channel to move to the FAILED
state. Ably SDKs will not attempt any automatic recovery actions. For example, when attempting to attach to a channel, with a token that doesn’t have the subscribe
capability for that channel, will cause that channel to enter the FAILED
state.
Whilst fatal errors won’t get better on their own, they are fixable. For example, if a channel goes into the FAILED
state due to the client not having the right capabilities to attach to it, that client could call authorize()
to obtain a new token which does have the right capabilities, then call attach()
on the channel. The library will not automatically reattach in the FAILED
state, however explicit calls to attach()
will make the client try again.
Non-fatal errors
Some types of errors are non-fatal. For example, a client may have network connectivity issues, or a channel may experience a loss of strict message continuity. Ably SDKs will automatically attempt to recover from these events. If channel continuity is lost in the process, the library will notify you through a resumed
flag in the ATTACHED
or UPDATE
event, so that you can decide how to handle the failure.
For every channel ATTACHED
and UPDATE
event, the ChannelStateChange
object contains a resumed
attribute. When true
, there has been no loss of continuity from the last time the channel was attached. When false
, there has been a loss of continuity.
For example:
- The first time a client attaches to a channel on a fresh connection,
resumed
will befalse
, as there was nothing to continue from. - If a client successfully recovers a connection and reattaches to its channels, the
resumed
flag on theATTACHED
events will tell it whether message continuity was preserved, or not. Any channel for which it’strue
, is guaranteed to receive every message it missed while the client was disconnected. - If a client resumes or recovers a connection unsuccessfully continuity is lost and the client receives a fresh connection. This generally happens because the client was disconnected for more than two minutes, which is how long Ably holds connection state for. If the client were resuming, all the channels (which will have gone into the
SUSPENDED
state after two minutes) will still reattach automatically, and the client will receiveATTACHED
events withresumed
set tofalse
. - If Ably needs to signal a loss of message continuity on an attached channel, the client will receive an
UPDATE
event withresumed
set tofalse
. This occurs in situations such as a partially successful resume, where the client was disconnected for less than two minutes.