Channel states

Channels transition through multiple states throughout their lifecycle. Understanding under which conditions the state of a channel changes, and managing those changes, is important to ensure that your applications behave as expected.

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.

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 when subscribing. 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.

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

Although attach() can be called explicitly, it is more common for a client to subscribe directly to a channel, which will automatically initiate the attach. This is also known as an implicit attachment.

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:

Select...
const channel = realtime.channels.get('chatroom'); await channel.attach();
Copied!

Normally, errors in attaching to a channel are communicated through the attach() callback. For implicit attaches, where a client only calls subscribe(), there is no callback.In these instances, listen for channel state changes to monitor errors.

This is also true in other cases where a channel is attached or re-attached automatically, for example, following the library reconnecting to Ably after a period of time in the suspended state.

A client can detach from a channel so that it no longer receives any messages published to it. 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.

The following is an example of detaching from a channel:

Select...
const channel = realtime.channels.get('chatroom'); await channel.detach();
Copied!

The Channel object is an EventEmitter. Events are emitted with a name that corresponds to the new channel state, whenever there is a change in the state of a channel.

Register a listener to monitor the current channel state. This can be a listener for the first occurrence, using once(), or for every change using on().

Use the on() method to register a listener for a specific channel state:

Select...
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'); });
Copied!

You can also use the on() method to register a listener for all channel state changes:

Select...
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);
Copied!

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.

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.

Use the off() method to remove listeners:

Select...
/* remove the listener registered for a single event */ channel.off('attached', myListener); /* remove the listener registered for all events */ channel.off(myListener);
Copied!

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. This leads to an update event being emitted, with both current and previous set to attached and the resumed flag set to false.

If you receive 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.

Connection state also impacts the state of a channel in the following ways:

  • If the connection state becomes CLOSED, all channels will become DETACHED
  • If the connection state becomes FAILED, all channels will become FAILED
  • If the connection state becomes SUSPENDED, all previously-ATTACHED or ATTACHING channels will become SUSPENDED
  • If the connection state becomes CONNECTED, any channels that were SUSPENDED will be automatically reattached

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().

Select...
const channel = realtime.channels.get('private:chatroom'); channel.on('failed', (stateChange) => { console.log('Channel failed, reason: ', stateChange.reason); }); await channel.attach();
Copied!

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.

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 be false, as there was nothing to continue from.
  • If a client successfully recovers a connection and reattaches to its channels, the resumed flag on the ATTACHED events will tell it whether message continuity was preserved, or not. Any channel for which it’s true, 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 receive ATTACHED events with resumed set to false.
  • If Ably needs to signal a loss of message continuity on an attached channel, the client will receive an UPDATE event with resumed set to false. This occurs in situations such as a partially successful resume, where the client was disconnected for less than two minutes.
States
v2.0