A simple example demonstrating how using deltas compares to not using deltas.
+
This example by default is subscribed to the CTtransit bus source, found on the Ably Hub. If you want to test this on one of your own channels, you can replace the API key with one of your own.
+
+
Cumulative size of messages sent without deltas: 0 Bytes
+
Cumulative size of messages sent with deltas: 0 Bytes
In this example, we demonstrate the simplest way to subscribe to a message using rewind in libraries older than v1.2. See our Rewind documentation for more details.
+
In this example, we demonstrate the simplest way to subscribe to a message using rewind in libraries older than v1.2. See our Rewind documentation for more details.
diff --git a/content/concepts/long-polling.textile b/content/concepts/long-polling.textile
index 07c125cf01..1b4765cb71 100644
--- a/content/concepts/long-polling.textile
+++ b/content/concepts/long-polling.textile
@@ -112,7 +112,7 @@ From the readme, "Pollymer is a general-purpose AJAX library that provides conve
Optional extras include support for JSON-P and logging.
-```[js]
+```[javascript]
var req = new Pollymer.Request();
req.on('finished', function(code, result, headers) { ... });
req.on('error', function(reason) { ... });
diff --git a/content/mqtt/index.textile b/content/mqtt/index.textile
index 4ff76badc7..8775ad4404 100644
--- a/content/mqtt/index.textile
+++ b/content/mqtt/index.textile
@@ -35,8 +35,8 @@ For example, in the NodeJS "MQTT package":https://www.npmjs.com/package/mqtt, yo
bc[nodejs]. {
var options = {
keepalive: 30,
- username: 'FIRST_HALF_OF_API_KEY',
- password: 'SECOND_HALF_OF_API_KEY',
+ username: '{{API_KEY_NAME}}', /* API key's name */
+ password: '{{API_KEY_SECRET}}', /* API key's secret */
port: 8883
};
var client = mqtt.connect('mqtts:mqtt.ably.io', options);
@@ -54,8 +54,8 @@ bc[nodejs]. {
var decoder = new encoding.TextDecoder();
var options = {
keepalive: 30,
- username: 'FIRST_HALF_OF_API_KEY',
- password: 'SECOND_HALF_OF_API_KEY',
+ username: '{{API_KEY_NAME}}', /* API key's name */
+ password: '{{API_KEY_SECRET}}', /* API key's secret */
port: 8883
};
var client = mqtt.connect('mqtts:mqtt.ably.io', options);
diff --git a/content/partials/types/_channel_options.textile b/content/partials/types/_channel_options.textile
index 2ff18322ef..0275861b4a 100644
--- a/content/partials/types/_channel_options.textile
+++ b/content/partials/types/_channel_options.textile
@@ -1,4 +1,4 @@
-Currently the supported channel options are only used for "configuring encryption":/realtime/encryption.
+Channel options are used for setting "channel parameters":/realtime/channels/channel-parameters/overview and "configuring encryption":/realtime/encryption.
blang[jsall].
@ChannelOptions@, a plain Javascript object, may optionally be specified when instancing a "@Channel@":/realtime/channels, and this may be used to specify channel-specific options. The following attributes can be defined on the object:
@@ -20,6 +20,9 @@ h4.
java: Members
ruby: Attributes
+blang[jsall,java,swift,objc,dotnet].
+ - paramsParams := Optional "parameters":/realtime/channels/channel-parameters/overview which specify behaviour of the channel. __Type: @Map@@JSON Object@__
+
- cipher:cipherCipherParams := Requests encryption for this channel when not null, and specifies encryption-related parameters (such as algorithm, chaining mode, key length and key). See "an example":/realtime/encryption#getting-started __Type: "@CipherParams@":/realtime/encryption#cipher-params or an options objecta @Param[]@ listan options hashan Associative Array containing at a minimum a @key@__
blang[java].
diff --git a/content/realtime/channel-metadata.textile b/content/realtime/channel-metadata.textile
index 8af2ac9b14..724532d489 100644
--- a/content/realtime/channel-metadata.textile
+++ b/content/realtime/channel-metadata.textile
@@ -1,7 +1,7 @@
---
title: Channel Metadata API
section: realtime
-index: 31
+index: 3
jump_to:
Help with:
- Overview#overview
diff --git a/content/realtime/channel-params.textile b/content/realtime/channel-params.textile
deleted file mode 100644
index d2e4d76e7f..0000000000
--- a/content/realtime/channel-params.textile
+++ /dev/null
@@ -1,247 +0,0 @@
----
-title: Channel Parameters
-section: realtime
-index: 32
-jump_to:
- Help with:
- - Overview#overview
- - Using params with v1.1 or earlier Ably libraries#using-params-with-lib-v11
- - Using params with non-Ably transports#using-params-with-other-transports
- - Examples#examples
- Parameters:
- - rewind#rewind
----
-
-h2(#overview). Overview
-
-Channel parameters are a general mechanism by which a client can express properties of a channel, or of its attachment to a channel. A set of channel parameters is simply a set of key/value pairs, where both keys and values are strings; the keys correspond to specific features that Ably defines.
-
-The methods provided for specifying channel parameters, and the currently available features, are outlined below.
-
-h2(#supported-params). Currently supported channel params
-
-- rewind := Used to request that an attachment start from some number of messages or point in time in the past. See "rewind":#rewind for more information
-- delta := **In an experimental state**. Used to request that data payloads should be sent as deltas to the previous payload. "Contact us":https://www.ably.io/contact for more information and supported values
-
-h2(#using-params-with-lib-v11). Using channel params with v1.1 or earlier Ably client libraries
-
-The current Ably libraries, at version 1.1, do not expose an API for expressing channel parameters. This means that it is necessary to specify parameters in a way that is opaque to the library.
-
-A set of params is expressed by including a query string, using standard URL query syntax and encoding, within the qualifier part of a channel name. The qualifier part is in square brackets at the start of the channel name.
-
-For example, to specify the parameter @foo@ with value @bar@ on channel @baz@, the qualified channel name would be @[?foo=bar]baz@. If the channel name already has a qualifier, such as @[meta]log@, then the query string follows the existing qualifier, as in @[meta?foo=bar]log@.
-
-Using this syntax with the Ably library means that channel parameters are specified for the lifetime of the @Channel@ instance; in order to reference the same channel, but with different channel parameters, it is necessary to get a new @Channel@ instance, using a qualified name that includes the new channel parameters.
-
-h3. Example
-
-For example, to specify the @rewind@ channel param with the value @"1"@:
-
-```[javascript]
- const realtime = new Ably.Realtime('{{API_KEY}}');
- const channel = realtime.channels.get('[?rewind=1]{{RANDOM_CHANNEL_NAME}}');
-```
-
-h2(#using-params-with-other-transports). Using params with non-Ably transports
-
-It is possible to interact with Ably channels using transports that do not involve using an Ably library; for example using SSE without any library, or using a supported non-Ably protocol such as MQTT. In these cases, it is also necessary to use a qualified channel name.
-
-A set of params is expressed by including a query string, using standard URL query syntax and encoding, within the qualifier part of a channel name. The qualifier part is in square brackets at the start of the channel name.
-
-For example, to specify the parameter @foo@ with value @bar@ on channel @baz@, the qualified channel name would be @[?foo=bar]baz@. If the channel name already has a qualifier, such as @[meta]log@, then the query string follows the existing qualifier, as in @[meta?foo=bar]log@.
-
-In an SSE connection, it is also possible to specify channel parameters as a query string in the connection URL, instead of as a qualifier on an individual channel name. In this case, the given channel parameters apply to all channel attachments associated with that connection.
-
-h3. SSE example
-
-For example, to specify the @rewind@ channel param with the value @"1"@ using a querystring parameter, where it will apply to all channels:
-
-```[javascript]
-var querystring = 'v=1.2&channels={{RANDOM_CHANNEL_NAME}}&rewind=1&key={{API_KEY}}';
-var eventSource = new EventSource('https://realtime.ably.io/event-stream?' + querystring);
-```
-
-Or to specify the same parameter but only applying to one channel of two, using a qualified channel name:
-
-```[javascript]
- var channelOne = encodeURIComponent('[?rewind=1]channel1');
- var channelTwo = 'channel2';
- var channels = channelOne + ',' + channelTwo;
- var querystring = 'v=1.2&key={{API_KEY}}&channels=' + channels';
- var eventSource = new EventSource('https://realtime.ably.io/event-stream?' + querystring);
-```
-
-h2(#rewind). Rewind parameter
-
-The @rewind@ channel parameter relates to the initial attachment of a connection to a channel, and expresses the intent to attach to the channel at a position, or a point in time, in the past (that is, effectively "rewinding" the channel for the purposes of the present attachment).
-
-A @rewind@ parameter can express a channel position in terms of a number of messages, or a time interval.
-
-A @rewind@ value that is simply a number @n@ (eg @rewind=1@) is a request to attach to the channel at a position @n@ messages before the present position. If that attachment is successful, and one or more messages exist on the channel prior to the present position, then those messages will be delivered to the subscriber immediately after the attachment has completed, and before any subsequent messages that arise in real time.
-
-If fewer than the requested number of messages exists on the channel (including the case that there are no prior messages), then the available messages are sent; this does not constitute an error.
-
-A @rewind@ value can also be a string that is a time interval specifier. Supported specifier values express an integral number of seconds (eg @15s@) or minutes (eg @2m@). If that attachment is successful, and one or more messages exist on the channel in the given time interval prior to the present time, then those messages will be delivered to the subscriber immediately after the attachment has completed, and before any subsequent messages that arise in real time.
-
-If you wish to use a time interval rewind but additionally specify a limit on the number of messages to be returned, you can use the @rewindLimit@ channel param. For example, to request up to 10 messages in a window 5m before the present time, specify a channel parameter string of @rewind=5m&rewindLimit=10@. If fewer than the requested number of messages exists on the channel in that interval (including the case that there are no messages), then the available messages are sent; this does not constitute an error.
-
-At most 100 messages will be sent in a rewind request. If the number of messages within the specified interval is greater than that limit, then only the most recent messages up to that limit are sent. The attachment succeeds, but truncation of the message backlog is indicated as a non-fatal error in the attachment response.
-
-By default, a maximum of two minutes of channel history is available when attaching. This means that a rewind time specifier of greater than two minutes will only be able to rewind by two minutes. If a channel has persistence enabled, then it is possible to rewind back in time by up to the persistence TTL on the channel.
-
-The channel position expressed by a @rewind@ parameter has an effect only on an initial channel attachment. Any subsequent reattachment of the same channel on the same connection, in order to resume the connection, will attempt to resume with continuity from the point at which the connection dropped. (There are a few exceptions to this: in particular, client libraries earlier than v1.2 that have been disconnected for over two minutes, and all clients when using "@recover@ mode":/realtime/connection#connection-state-recovery ; in both cases the previous attachment state is not preserved).
-
-Any @rewind@ parameter value that cannot be parsed either as a number or a time specifier represents an error, and any attachment request will fail with an error.
-
-h3(#rewind-example-ably). Rewind example with an Ably client library
-
-To subscribe to a channel, getting the most recent message if available:
-
-```[jsall]
- // only with ably-js v1.2 or later
- const realtime = new Ably.Realtime('{{API_KEY}}');
- realtime.channels.get('{{RANDOM_CHANNEL_NAME}}', {
- rewind: '1'
- }).subscribe(msg => console.log("Received message: ", msg));
-```
-
-```[jsall]
- // with ably-js v1.1 or below
- const realtime = new Ably.Realtime('{{API_KEY}}');
- const channel = realtime.channels.get('[?rewind=1]{{RANDOM_CHANNEL_NAME}}');
- channel.subscribe(msg => console.log("Received message: ", msg));
-```
-
-h3(#rewind-example-sse). Rewind example with SSE
-
-To subscribe to a channel, getting the most recent message if available:
-
-```[javascript]
- var querystring = 'v=1.2&channels={{RANDOM_CHANNEL_NAME}}&rewind=1&key={{API_KEY}}';
- var eventSource = new EventSource('https://realtime.ably.io/event-stream?' + querystring);
-```
-
-h3(#rewind-examples-mqtt). Rewind example with MQTT
-
-```[nodejs]
- var mqtt = require('mqtt');
- var options = {
- keepalive: 30,
- username: 'FIRST_HALF_OF_API_KEY',
- password: 'SECOND_HALF_OF_API_KEY',
- port: 8883
- };
- var client = mqtt.connect('mqtts:mqtt.ably.io', options);
- client.on('connect', () => {
- client.subscribe('[?rewind=1]{{RANDOM_CHANNEL_NAME}}');
- });
- client.on('message', (topic, message) => {
- ...
- });
-```
-
-h2(#delta). Delta parameter
-
-The @delta@ channel parameter allows subscribers to expresse their desire to receive deltas (diffs) between the previous and current message instead of the current message in full on a given channel. The effects of this parameter are invisible to message senders and only affect subscribers that specify it - i.e. the sender sends messages in full and Ably computes and sends the message deltas to any subscriber which subscribed using this parameter.
-
-Note that requesting deltas does not guarantee that every message received will be a delta, as the server may chose to send some messages in full depending on system load and other factors. Therefore a subscriber using the @delta@ parameter should be prepared to handle a random mix of delta and full messages.
-
-The @delta@ parameter can take only the value @vcdiff@. VCDIFF is the format of the deltas. It is an open format specified in "RFC 3284":https://tools.ietf.org/html/rfc3284.
-
-Ably provides a JavaScript "delta codec library":https://github.com/ably/delta-codec-js to help you avoid writing boilerplate VCDIFF handling code. Some examples follow.
-
-h3(#delta-example-sse). Delta example with enveloped SSE
-
-```[jsall]
-const key = '{{API_KEY}}';
-const channel = 'sample-app-sse';
-const url = `https://realtime.ably.io/event-stream?channels=${channel}&v=1.2&key=${key}&delta=vcdiff`;
-const eventSource = new EventSource(url);
-const channelDecoder = new DeltaCodec.CheckedVcdiffDecoder();
-
-eventSource.onmessage = (event) => {
- /* event.data is JSON-encoded Ably Message (see https://www.ably.io/documentation/realtime/types#message) */
- const message = JSON.parse(event.data);
- const { id, extras } = message;
- let { data } = message;
-
- try {
- if (extras && extras.delta) {
- data = channelDecoder.applyBase64Delta(data, id, extras.delta.from).asUtf8String();
- } else {
- channelDecoder.setBase(data, id);
- }
- } catch(e) {
- /* Delta decoder error */
- console.log(e);
- }
-
- /* Process decoded data */
- console.log(data);
-};
-```
-
-h3(#delta-example-unenv-sse). Delta example with unenveloped SSE
-
-```[jsall]
-const key = '{{API_KEY}}';
-const channel = 'sample-app-sse';
-const url = `https://realtime.ably.io/event-stream?channels=${channel}&v=1.2&key=${key}&delta=vcdiff&enveloped=false`;
-const eventSource = new EventSource(url);
-const channelDecoder = new DeltaCodec.VcdiffDecoder();
-
-eventSource.onmessage = (event) => {
- let data = event.data;
-
- try {
- if (DeltaCodec.VcdiffDecoder.isBase64Delta(data)) {
- data = channelDecoder.applyBase64Delta(data).asUtf8String();
- } else {
- channelDecoder.setBase(data);
- }
- } catch(e) {
- /* Delta decoder error */
- console.log(e);
- }
-
- /* Process decoded data */
- console.log(data);
-};
-```
-
-h3(#delta-example-mqtt). Delta example with MQTT
-
-```[jsall]
-const mqtt = require('mqtt');
-const { VcdiffDecoder } = require('./lib');
-
-const options = {
- keepalive: 30,
- username: 'FIRST_HALF_OF_API_KEY',
- password: 'SECOND_HALF_OF_API_KEY',
- port: 8883
-};
-const client = mqtt.connect('mqtts:mqtt.ably.io', options);
-const channelName = 'sample-app-mqtt';
-const channelDecoder = new VcdiffDecoder();
-
-client.on('message', (_, payload) => {
- let data = payload;
-
- try {
- if (VcdiffDecoder.isDelta(data)) {
- data = channelDecoder.applyDelta(data).asUint8Array();
- } else {
- channelDecoder.setBase(data);
- }
- } catch(e) {
- /* Delta decoder error */
- console.log(e);
- }
-
- /* Process decoded data */
- console.log(data);
-});
-
-client.subscribe(`[?delta=vcdiff]${channelName}`);
-```
diff --git a/content/realtime/channels.textile b/content/realtime/channels.textile
index 0332e35ed4..ceb83bb5d6 100644
--- a/content/realtime/channels.textile
+++ b/content/realtime/channels.textile
@@ -1,7 +1,7 @@
---
title: Channels
section: realtime
-index: 30
+index: 2
languages:
- javascript
- nodejs
@@ -140,9 +140,34 @@ bc[objc]. ARTRealtimeChannel *channel = [realtime.channels get:@"channelName"];
bc[swift]. let channel = realtime.channels.get("channelName")
-h4(#setting-channel). Setting channel options and encryption
+h4(#setting-channel-options). Setting channel options
-A set of "channel options":#channel-options may also be passed to configure a channel for encryption. Find out more about "symmetric message encryption":/realtime/encryption.
+You can set "channel options":#channel-options in one of two ways:
+
+* Initially, when you first obtain a channel using "channels.get":#obtaining-channel.
+* After creation, when you update a channel using "channels.setOptions":#modifying-options.
+
+h5(#setting-channel-parameters). Setting channel parameters
+
+Channel parameters allow a client to set the properties of a channel. The are two parameters that can be specified:
+
+- rewind := Used to request that an attachment start from a given number of messages or point in time in the past. See "rewind":/realtime/channels/channel-parameters/rewind for more information.
+- delta := Used to request that data payloads should be sent as deltas to the previous payload. See "deltas":/realtime/channels/channel-parameters/deltas for more information.
+
+bc[jsall]. const realtime = new Ably.Realtime('{{API_KEY}}');
+const channelOptions = {
+ params: {
+ delta: 'vcdiff',
+ rewind: '1'
+ }
+};
+const channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}', channelOptions);
+
+h5(#setting-encryption-options). Setting channel encryption options
+
+You can enable encryption on a channel using the "channel options":#channel-options.
+
+Find out more about "symmetric message encryption":/realtime/encryption.
bc[jsall]. Ably.Realtime.Crypto.generateRandomKey(function(err, key) {
var options = { cipher: { key: key } };
@@ -238,14 +263,14 @@ channel.subscribe { message in
h4(#rewind). Subscribing to a channel with the rewind option enabled
-It is possible to subscribe to a channel and obtain messages from that channel's "history":/realtime/history with a single API request. To do so you would use the "rewind":/realtime/channel-params#rewind feature. This will result in the specified number of messages from "history":/realtime/history being returned as part of the subscription process.
+It is possible to subscribe to a channel and obtain messages from that channel's "history":/realtime/history with a single API request. To do so you would use the "rewind":/realtime/channels/channel-parameters/rewind feature. This will result in the specified number of messages from "history":/realtime/history being returned as part of the subscription process.
minimize. View details
- "Rewind":/realtime/channel-params has two methods of obtaining messages from history. You can retrieve a specified number of messages from history or messages from a set period of time into the past.
+ "Rewind":/realtime/channels/channel-parameters has two methods of obtaining messages from history. You can retrieve a specified number of messages from history or messages from a set period of time into the past.
- In current Ably libraries, or when using the "service without a library":https://www.ably.io/adapters, this is done by qualifying the channel name. There is a future library release planned in which it will be possible to specify channel params directly via the API. As an example, if you have no metadata and wish to subscribe to channel @my_channel@ and fetch the most recent message from "history":/realtime/history, you would specify the channel as @[?rewind=1]my_channel@. If the channel had some metadata, @[some_metadata]my_channel@, you would apply "rewind":/realtime/channel-params with @[some_metadata?rewind=1]my_channel@. For more details, look at our "channel parameter":/realtime/channel-params documentation.
+ In current Ably libraries, or when using the "service without a library":https://www.ably.io/adapters, this is done by qualifying the channel name. There is a future library release planned in which it will be possible to specify channel params directly via the API. As an example, if you have no metadata and wish to subscribe to channel @my_channel@ and fetch the most recent message from "history":/realtime/history, you would specify the channel as @[?rewind=1]my_channel@. If the channel had some metadata, @[some_metadata]my_channel@, you would apply "rewind":/realtime/channels/channel-parameters/rewind with @[some_metadata?rewind=1]my_channel@. For more details, look at our "channel parameter":/realtime/channels/channel-parameters documentation.
- *Note* that "rewind":/realtime/channel-params is limited to a maximum of @100 messages@, and can only get messages no older than 2 minutes by default. If a channel has persistence enabled, then it is possible to rewind back in time by up to the persistence TTL on the channel.
+ *Note* that "rewind":/realtime/channels/channel-parameters/rewind is limited to a maximum of @100 messages@, and can only get messages no older than 2 minutes by default. If a channel has persistence enabled, then it is possible to rewind back in time by up to the persistence TTL on the channel.
In addition, rewind will only apply upon *attaching* to a channel, so any subsequent subscriptions post-attach will not fetch old messages.
@@ -515,6 +540,10 @@ channel.publish("action", data: "boom!")
Normally, errors in attaching to a channel are communicated through the attach callback. For implicit attaches (and other cases where a channel is attached or reattached automatically, e.g. following the library reconnecting after a period in the @suspended@ state), there is no callback, so if you want to know what happens, you'll need to listen for channel state changes.
+h3(#modifying-options). Modifying channel options
+
+You can modify the @ChannelOptions@ associated with a given channel instance by calling @setOptions@ and passing a new @ChannelOptions@. The modified options will either take effect at the time of attachment (if an attach for that channel has not yet been initiated), or the @setOptions@ call will trigger an immediate attach operation to apply the modified options. Success or failure of any triggered attach operation triggered is indicated in the result of the @setOptions@ call.
+
h3(#multi-publish). Publishing to multiple channels
Often it is necessary to publish a single message in multiple channels at the same time. In the realtime API, this is achieved simply by making multiple separate "publish":#publish requests. If a separate publish is made in each of the channels in question, the realtime protocol will allow for those concurrent requests to be in-flight simultaneously. This ensures that a publish on a channel is not delayed waiting for completion of operations in other channels.
diff --git a/content/realtime/channels/channel-parameters/deltas.textile b/content/realtime/channels/channel-parameters/deltas.textile
new file mode 100644
index 0000000000..cbeaaabcae
--- /dev/null
+++ b/content/realtime/channels/channel-parameters/deltas.textile
@@ -0,0 +1,253 @@
+---
+title: Delta compression
+section: realtime
+index: 6
+languages:
+ - javascript
+ - nodejs
+ - java
+ - swift
+ - csharp
+jump_to:
+ Help with:
+ - Overview#overview
+ - Delta processing#delta-processing
+ - Using deltas#using-deltas
+---
+
+h2(#overview). Overview
+
+The @delta@ parameter enables delta compression. It is applied on the channel you are subscribing to, enabling delta mode.
+
+Delta mode is a way for a client to subscribe to a channel so that message payloads sent contain only the difference (ie the delta) between the present message and the previous message on the channel.
+
+This is useful for channels that carry messages representing a series of updates to a particular object or document with a significant degree of similarly between successive messages. The client can apply the delta to the previous message to obtain the full payload. Using delta mode can significantly reduce the encoded size of each message in the case that message payloads change by differences that are small relative to the size of the value. This reduction in size can reduce bandwidth costs, reduce transit latencies, and enable greater message throughput on a connection.
+
+The delta mode implementation supports a single representation of a delta, "VCDIFF":https://tools.ietf.org/html/rfc3284.
+
+
+
+
+
+Subscribing in delta mode is enabled for a given channel by specifying a @delta@ "channel parameter":/realtime/channels/channel-parameters/overview with the value @vcdiff@. This will cause delta messages to be generated by the server and sent to the client, and the library reconstitutes the original message payload. Messages on the channel are delivered to the subscriber's listener in the same way as with a normal subscription.
+
+h2(#delta-processing). Delta processing
+
+Deltas apply to the principal payload of a "@Message@":/realtime/messages#properties published via Ably, which is the @data@ member. Other elements of a message, such as @clientId@, @name@, or @extras@ are unchanged by use of deltas and are not compressed.
+
+Deltas are supported for realtime subscriptions only. Messages retrieved via "history":/realtime/history, and messages delivered to "Reactor integrations":https://www.ably.io/reactor, are not compressed.
+
+Delta compression via @vcdiff@ is supported for all payloads, whether string, binary, or JSON-encoded. The delta algorithm processes message payloads as opaque binaries and has no dependency on the structure of the payload - it does not process line-oriented diffs, for example. In principle, @vcdiff@ deltas can be applied to encrypted message payloads, but in practice this provides no benefit because there is no similarity between successive encrypted payloads even on identical or near-identical plaintext message payloads.
+
+Delta compression is a subscriber-specified option only - the publisher has no control over whether or not deltas are generated for any given message; the processing is performed if there is at least one subscriber on a channel that has requested a delta-mode subscription.
+
+There is no constraint on how many publishers or subscribers there are. If there are multiple publishers, then deltas can still be generated, and they will be determined based on the order of messages in the channel in question.
+
+Delta mode, when activated on a channel, is performed for all messages on a channel, and deltas are calculated strictly based on the message ordering in that channel. The effectiveness of delta mode is dependent on the level of similarity between successive payloads.
+
+If a delta is generated and it results in a difference that is not appreciably smaller than the original message, or is even larger than the original message (which can happen if successive messages are completely different), then the delta will not be sent and clients will receive the original, unprocessed message. Therefore, the sequence of messages that will be delivered to a client for any given channel will be a combination of regular messages and delta-compressed messages.
+
+A channel subscriber can experience a discontinuity in the sequence of messages it receives on a given channel for the following reasons:
+
+* The connection can drop, and there will be a discontinuity of the client is unable to reconnect within the two-minute window it is allowed to preserve connection continuity.
+* The outbound connection might have been rate-limited, which causes some messages to be dropped.
+* There might have been some internal error in the Ably system which leads to the server being unable to preserve continuity on the channel.
+
+In these cases, the service indicates the discontinuity to the client, together with the reason, and this is usually visible to the subscriber in a channel @UPDATE@ event.
+
+If a subscriber has a delta-mode subscription and the channel in question experiences a discontinuity, then a non-delta message will be delivered to the client as the first message after the discontinuity. This ensures that lost messages do not prevent the client from reconstituting messages from deltas.
+
+h2(#using-deltas). Using deltas
+
+h3(#using-deltas-ably). Via an Ably library
+
+The most common way to subscribe to Ably channels is via a realtime connection, using an Ably realtime library.
+
+For many libraries this requires no change on the part of the caller except to specify the @delta@ "channel parameter":/realtime/channels/channel-parameters/overview when subscribing to the channel. In some libraries, the @vcdiff@ delta decoding library is excluded from the default library distribution in order to avoid bloating the library. In these cases, it is also necessary to supply the delta decoder plugin when instancing the Ably library.
+
+```[javascript](code-editor:realtime/channel-deltas)
+ /* Make sure to include in your head */
+ var realtime = new Ably.Realtime({key: '{{API_KEY}}', plugins: {vcdiffDecoder: vcdiffDecoder}});
+ realtime.channels.get('{{RANDOM_CHANNEL_NAME}}', {
+ delta: 'vcdiff'
+ }).subscribe(msg => console.log("Received message: ", msg));
+```
+
+```[nodejs]
+ var vcdiffPlugin = require('@ably/vcdiff-decoder');
+ var realtime = new Ably.Realtime({key: '{{API_KEY}}', plugins: {vcdiffDecoder: vcdiffDecoder}});
+ realtime.channels.get('{{RANDOM_CHANNEL_NAME}}', {
+ delta: 'vcdiff'
+ }).subscribe(msg => console.log("Received message: ", msg));
+```
+
+```[java]
+ AblyRealtime ably = new AblyRealtime("{{API_KEY}}")
+ Channel channel = ably.channels.get("{{RANDOM_CHANNEL_NAME}}", new ChannelOptions{{params = Map.of("delta", "vcdiff")}});
+ channel.subscribe(new MessageListener() {
+ @Override
+ public void onMessage(Message message) {
+ System.out.println("Received `" + message.name + "` message with data: " + message.data);
+ }
+ });
+```
+
+```[swift]
+ let options = ARTClientOptions(key: key)
+ let client = ARTRealtime(options: options)
+ let channelOptions = ARTRealtimeChannelOptions()
+ channelOptions.params = [
+ "delta": "vcdiff"
+ ]
+
+ let channel = client.channels.get(channelName, options: channelOptions)
+```
+
+```[csharp]
+ var clientOptions = new ClientOptions();
+ clientOptions.Key = "{{API_KEY}}";
+ clientOptions.Environment = AblyEnvironment;
+ var ably = new AblyRealtime(clientOptions);
+
+ var channelParams = new ChannelParams();
+ channelParams.Add("delta", "vcdiff");
+ var channelOptions = new ChannelOptions();
+ channelOptions.Params = channelParams;
+ var channel = ably.Channels.Get("{{RANDOM_CHANNEL_NAME}}", channelOptions);
+
+ channel.Subscribe(message => {
+ Console.WriteLine(message.Data.ToString());
+ });
+```
+
+h3(#using-deltas-non-ably). Via a subscription that does not use an Ably library
+
+If subscribing to a channel in delta mode using "SSE":/sse or one of the protocol adaptors such as "MQTT":/mqtt, then you will need to decode any received delta messages yourself. There are decoder libraries available to do this for several platforms; see the "download":https://www.ably.io/download section for details.
+
+When subscribing without an Ably library, the channel @delta@ parameter must be specified using a "qualified channel name":/realtime/channels/channel-parameters/overview. In the case of SSE, it is also possible to specify channel parameters as regular query parameters on the connection URL.
+
+Some transports provide raw message payloads - that is, the content of the @data@ attribute of a @Message@ - without the accompanying metadata. That means that the recipient of the message does not have access to the @extras@ or @encoding@ attributes of the message that would ordinarily be used to decode delta message payloads.
+
+Examples of such transports are MQTT, and SSE in non-enveloped mode. In order to assist applications that use these transports, the @vcdiff@ decoder libraries can check for the @vcdiff@ header at the start of the message payload as an inexact method of determining whether or not the message is a regular message or a delta. Note that, in order to rely on that check, you need to know that that header will not be present in any valid (uncompressed) message in your app. No valid JSON value, for example, will match the @vcdiff@ header check, so it is safe to perform this sniffing on JSON message payloads.
+
+h4(#delta-example-sse). Delta example with SSE
+
+You can subscribe to messages in delta mode, using the SSE transport, as follows.
+
+```[javascript]
+var key = '{{API_KEY}}';
+var channel = 'sample-app-sse';
+var baseUrl = 'https://realtime.ably.io/event-stream';
+var urlParams = `?channels=${channel}&v=1.1&key=${key}&delta=vcdiff`;
+var url = baseUrl + urlParams;
+var eventSource = new EventSource(url);
+var channelDecoder = new DeltaCodec.CheckedVcdiffDecoder();
+
+eventSource.onmessage = function(event) {
+ /* event.data is JSON-encoded Ably Message
+ (see https://www.ably.io/documentation/realtime/types#message) */
+ var message = JSON.parse(event.data);
+ var { id, extras } = message;
+ var { data } = message;
+
+ try {
+ if (extras && extras.delta) {
+ data = channelDecoder.applyBase64Delta(data, id, extras.delta.from).asUtf8String();
+ } else {
+ channelDecoder.setBase(data, id);
+ }
+ } catch(e) {
+ /* Delta decoder error */
+ console.log(e);
+ }
+
+ /* Process decoded data */
+ console.log(data);
+};
+```
+
+h4(#delta-example-unenv-sse). Delta example with unenveloped SSE
+
+For more information on enveloped and uneveloped SSE, please see the "SSE API":/sse#sse
+
+```[javascript]
+ /* Make sure to include in your head */
+ var DeltaCodec = require('@ably/delta-codec');
+
+ var key = '{{API_KEY}}';
+ var channel = 'sample-app-sse';
+ var baseUrl = 'https://realtime.ably.io/event-stream';
+ var urlParams = `?channels=${channel}&v=1.1&key=${key}&delta=vcdiff&enveloped=false`;
+ var url = baseUrl + urlParams;
+ var eventSource = new EventSource(url);
+ var channelDecoder = new DeltaCodec.VcdiffDecoder();
+
+ eventSource.onmessage = function(event) {
+ var data = event.data;
+
+ try {
+ if (DeltaCodec.VcdiffDecoder.isBase64Delta(data)) {
+ data = channelDecoder.applyBase64Delta(data).asUtf8String();
+ } else {
+ channelDecoder.setBase(data);
+ }
+ } catch(e) {
+ /* Delta decoder error */
+ console.log(e);
+ }
+
+ /* Process decoded data */
+ console.log(data);
+ };
+```
+
+h4(#delta-example-mqtt). Delta example with MQTT
+
+```[nodejs]
+ var mqtt = require('mqtt');
+ var { VcdiffDecoder } = require('@ably/vcdiff-decoder');
+
+ var options = {
+ keepalive: 30,
+ username: '{{API_KEY_NAME}}', /* API key's name */
+ password: '{{API_KEY_SECRET}}', /* API key's secret */
+ port: 8883
+ };
+ var client = mqtt.connect('mqtts:mqtt.ably.io', options);
+ var channelName = 'sample-app-mqtt';
+ var channelDecoder = new VcdiffDecoder();
+
+ client.on('message', (_, payload) => {
+ var data = payload;
+
+ try {
+ if (VcdiffDecoder.isDelta(data)) {
+ data = channelDecoder.applyDelta(data).asUint8Array();
+ } else {
+ channelDecoder.setBase(data);
+ }
+ } catch(e) {
+ /* Delta decoder error */
+ console.log(e);
+ }
+
+ /* Process decoded data */
+ console.log(data);
+ });
+
+ client.subscribe(`[?delta=vcdiff]${channelName}`);
+```
+
+h1. API Reference
+
+inline-toc.
+ ChannelsOptions Details:
+ - ChannelOptions#channel-options
+
+h3(#channel-options).
+ default: ChannelOptions Object
+ objc,swift: ARTChannelOptions
+ java: io.ably.lib.types.ChannelOptions
+ csharp: IO.Ably.Realtime.ChannelOptions
+
+<%= partial partial_version('types/_channel_options') %>
diff --git a/content/realtime/channels/channel-parameters/overview.textile b/content/realtime/channels/channel-parameters/overview.textile
new file mode 100644
index 0000000000..992afc076b
--- /dev/null
+++ b/content/realtime/channels/channel-parameters/overview.textile
@@ -0,0 +1,188 @@
+---
+title: Channel Parameters
+section: realtime
+index: 4
+languages:
+ - javascript
+ - nodejs
+ - java
+ - swift
+ - csharp
+jump_to:
+ Help with:
+ - Overview#overview
+ - Supported channel parameters#supported-parameters
+ - Using channel parameters with Ably libraries#using-parameters-ably
+ - Using channel parameters outside of supported Ably libraries#using-parameters-non-ably
+ - Next steps#next-steps
+---
+
+h2(#overview). Overview
+
+Ably provides channel parameters as a means of customizing channel functionality. For example, you can request that a channel attachment start from some time in the past by using the "rewind parameter":#supported-parameters.
+
+The methods provided for specifying channel parameters are outlined below.
+
+h2(#supported-parameters). Supported channel parameters
+
+A set of channel parameters is a set of key/value pairs, where both keys and values are strings; the keys correspond to specific features that Ably defines:
+
+- rewind := Allows an attachment to a channel to start from a given number of messages or point in time in the past. See "rewind":/realtime/channels/channel-parameters/rewind for more information.
+- delta := Enables delta compression, a way for a client to subscribe to a channel so that message payloads sent contain only the difference (ie the delta) between the present message and the previous message on the channel. See "deltas":/realtime/channels/channel-parameters/deltas for more information.
+
+h2(#using-parameters-ably). Using channel parameters with Ably libraries
+
+You can specify channel parameters in the "@ChannelOptions@":#channel-options when obtaining a @Channel@. A collection of channel parameters is expressed as a map of string key/value pairs. The @ChannelOptions@ associated with a channel may also be updated by calling "setOptions":/realtime/channels#modifying-options. The parameters associated with a channel take effect when the channel is first attached; if the parameters are subsequently modified via a call to @setOptions@, then that call triggers an attach operation that applies the updated parameters, if successful.
+
+h3. Example
+
+For example, to specify the @rewind@ channel parameter with the value @"1"@:
+
+```[jsall]
+ var realtime = new Ably.Realtime('{{API_KEY}}');
+ var channelOpts = {params: {rewind: '1'}};
+ var channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}', channelOpts);
+```
+
+```[java]
+ final Map params = new HashMap<>();
+ params.put("rewind", "1");
+ final ChannelOptions options = new ChannelOptions();
+ options.params = params;
+ final Channel channel = ably.channels.get("{{RANDOM_CHANNEL_NAME}}", options);
+```
+
+```[swift]
+ let options = ARTClientOptions(key: key)
+ let client = ARTRealtime(options: options)
+ let channelOptions = ARTRealtimeChannelOptions()
+ channelOptions.params = [
+ "rewind": "1"
+ ]
+
+ let channel = client.channels.get(channelName, options: channelOptions)
+```
+
+```[csharp]
+ var clientOptions = new ClientOptions();
+ clientOptions.Key = "{{API_KEY}}";
+ clientOptions.Environment = AblyEnvironment;
+ var ably = new AblyRealtime(clientOptions);
+
+ var channelParams = new ChannelParams();
+ channelParams.Add("rewind", "1");
+ var channelOptions = new ChannelOptions();
+ channelOptions.Params = channelParams;
+ var channel = ably.Channels.Get("{{RANDOM_CHANNEL_NAME}}", channelOptions);
+
+ channel.Subscribe(message => {
+ Console.WriteLine(message.Data.ToString());
+ });
+```
+
+To modify the @rewind@ channel parameters with the value @"15s"@:
+
+```[jsall]
+ var realtime = new Ably.Realtime('{{API_KEY}}');
+ var channelOpts = {params: {rewind: '15s'}}
+ channel.setOptions(channelOpts, (err) => {
+ if(!err) {
+ console.log('channel params updated');
+ }
+ });
+```
+
+```[java]
+ final Map params = new HashMap<>();
+ params.put("rewind", "15s");
+ final ChannelOptions options = new ChannelOptions();
+ options.params = params;
+ final Channel channel = ably.channels.get("{{RANDOM_CHANNEL_NAME}}", options);
+```
+
+```[swift]
+ let options = ARTClientOptions(key: key)
+ let client = ARTRealtime(options: options)
+ let channelOptions = ARTRealtimeChannelOptions()
+ channelOptions.params = [
+ "rewind": "15s"
+ ]
+
+ let channel = client.channels.get(channelName, options: channelOptions)
+```
+
+```[csharp]
+ var clientOptions = new ClientOptions();
+ clientOptions.Key = "{{API_KEY}}";
+ clientOptions.Environment = AblyEnvironment;
+ var ably = new AblyRealtime(clientOptions);
+
+ var channelParams = new ChannelParams();
+ channelParams.Add("rewind", "15s");
+ var channelOptions = new ChannelOptions();
+ channelOptions.Params = channelParams;
+ var channel = ably.Channels.Get("{{RANDOM_CHANNEL_NAME}}", channelOptions);
+
+ channel.Subscribe(message => {
+ Console.WriteLine(message.Data.ToString());
+ });
+```
+
+h2(#using-parameters-non-ably). Using channel parameters without Ably library support
+
+For the client libraries that do not currently expose the API, or transports that do not involve using an Ably library, a set of channel parameters can be expressed by including a query string with standard URL query syntax and encoding, within the qualifier part of a channel name. The qualifier part is in square brackets at the start of the channel name.
+
+Examples of transports that do not use Ably libraries include using SSE without any library, or using a supported non-Ably protocol such as MQTT
+
+To specify the parameter @foo@ with value @bar@ on channel @baz@, the qualified channel name would be @[?foo=bar]baz@. If the channel name already has a qualifier, such as @[meta]log@, then the query string follows the existing qualifier, as in @[meta?foo=bar]log@.
+
+h3. Example of Ably library without channel parameters support
+
+Using this syntax with a non-supported Ably library means that channel parameters are specified for the lifetime of the @Channel@ instance; in order to reference the same channel, but with different channel parameters, it is necessary to get a new @Channel@ instance, using a qualified name that includes the new channel parameters.
+
+For example, to specify the @rewind@ channel parameter with the value @"1"@:
+
+```[javascript]
+ const realtime = new Ably.Realtime('{{API_KEY}}');
+ const channel = realtime.channels.get('[?rewind=1]{{RANDOM_CHANNEL_NAME}}');
+```
+
+h3. SSE example
+
+In an SSE connection, it is also possible to specify channel parameters as a query string in the connection URL, instead of as a qualifier on an individual channel name. In this case, the given channel parameters apply to all channel attachments associated with that connection.
+
+For example, to specify the @rewind@ channel parameter with the value @"1"@ using a querystring parameter, where it will apply to all channels:
+
+```[javascript]
+ var querystring = 'v=1.2&channels={{RANDOM_CHANNEL_NAME}}&rewind=1&key={{API_KEY}}';
+ var eventSource = new EventSource('https://realtime.ably.io/event-stream?' + querystring);
+```
+
+Or to specify the same parameter but only applying to one channel of two, using a qualified channel name:
+
+```[javascript]
+ var channelOne = encodeURIComponent('[?rewind=1]channel1');
+ var channelTwo = 'channel2';
+ var channels = channelOne + ',' + channelTwo;
+ var querystring = 'v=1.2&key={{API_KEY}}&channels=' + channels';
+ var eventSource = new EventSource('https://realtime.ably.io/event-stream?' + querystring);
+```
+
+h2(#next-steps). Next steps
+
+* Request that an attachment start from a given number of messages or point in time in the past using "rewind":/realtime/channels/channel-parameters/rewind.
+* Request that data payloads should be sent as deltas to the previous payload using "deltas":/realtime/channels/channel-parameters/deltas.
+
+h1. API Reference
+
+inline-toc.
+ ChannelsOptions Details:
+ - ChannelOptions#channel-options
+
+h3(#channel-options).
+ default: ChannelOptions Object
+ objc,swift: ARTChannelOptions
+ java: io.ably.lib.types.ChannelOptions
+ csharp: IO.Ably.Realtime.ChannelOptions
+
+<%= partial partial_version('types/_channel_options') %>
diff --git a/content/realtime/channels/channel-parameters/rewind.textile b/content/realtime/channels/channel-parameters/rewind.textile
new file mode 100644
index 0000000000..06aed44910
--- /dev/null
+++ b/content/realtime/channels/channel-parameters/rewind.textile
@@ -0,0 +1,145 @@
+---
+title: Rewind
+section: realtime
+index: 5
+languages:
+ - javascript
+ - nodejs
+ - java
+ - csharp
+ - swift
+jump_to:
+ Help with:
+ - Overview#overview
+ - Examples#examples
+ - Additional information#additional-info
+---
+
+h2(#overview). Overview
+
+The @rewind@ parameter allows you to specify, at the time of attaching to a channel, where to start the attachment from.
+
+You can specify either:
+
+* A given number of messages.
+* A point in time in the past, as a time interval.
+
+The parameter relates to the initial attachment of a connection to a channel, and expresses the intent to attach to the channel at a position, or a point in time, in the past (that is, effectively "rewinding" the channel for the purposes of the present attachment).
+
+The @rewind@ parameter is specified using "channel parameters":/realtime/channels/channel-parameters/overview.
+
+h2(#examples). Examples
+
+h3(#rewind-example-ably). Rewind example with an Ably client library
+
+A @rewind@ value that is a number @n@ (eg @rewind=1@) is a request to attach to the channel at a position @n@ messages before the present position. If the attachment is successful, and one or more messages exist on the channel prior to the present position, then those messages will be delivered to the subscriber immediately after the attachment has completed, and before any subsequent messages that arise in real time.
+
+If fewer than the requested number of messages exists on the channel (including the case that there are no prior messages), then the available messages are sent; this does not constitute an error.
+
+To subscribe to a channel, getting the most recent message if available:
+
+```[jsall]
+ var realtime = new Ably.Realtime('{{API_KEY}}');
+ realtime.channels.get('{{RANDOM_CHANNEL_NAME}}', {
+ rewind: '1'
+ }).subscribe(msg => console.log("Received message: ", msg));
+```
+
+```[java]
+ final Map params = new HashMap<>();
+ params.put("rewind", "1");
+ final ChannelOptions options = new ChannelOptions();
+ options.params = params;
+ final Channel channel = ably.channels.get("{{RANDOM_CHANNEL_NAME}}", options);
+
+ channel.subscribe(new MessageListener() {
+ @Override
+ public void onMessage(Message message) {
+ System.out.println("Received `" + message.name + "` message with data: " + message.data);
+ }
+ });
+```
+
+```[swift]
+ let options = ARTClientOptions(key: "{{API_KEY}}")
+ let client = ARTRealtime(options: options)
+ let channelOptions = ARTRealtimeChannelOptions()
+ channelOptions.params = [
+ "rewind": "1"
+ ]
+
+ let channel = client.channels.get(channelName, options: channelOptions)
+```
+
+```[csharp]
+ var clientOptions = new ClientOptions();
+ clientOptions.Key = "{{API_KEY}}";
+ clientOptions.Environment = AblyEnvironment;
+ var ably = new AblyRealtime(clientOptions);
+
+ var channelParams = new ChannelParams();
+ channelParams.Add("rewind", "1");
+ var channelOptions = new ChannelOptions();
+ channelOptions.Params = channelParams;
+ var channel = ably.Channels.Get("{{RANDOM_CHANNEL_NAME}}", channelOptions);
+
+ channel.Subscribe(message => {
+ Console.WriteLine(message.Data.ToString());
+ });
+```
+
+A @rewind@ value can also be a string that is a time interval specifier. Supported specifier values express an integral number of seconds (eg @15s@) or minutes (eg @2m@). If that attachment is successful, and one or more messages exist on the channel in the given time interval prior to the present time, then those messages will be delivered to the subscriber immediately after the attachment has completed, and before any subsequent messages that arise in real time.
+
+If you wish to use a time interval rewind but additionally specify a limit on the number of messages to be returned, you can use the @rewindLimit@ channel parameter. For example, to request up to 10 messages in a window 5m before the present time, specify a channel parameter string of @rewind=5m&rewindLimit=10@. If fewer than the requested number of messages exists on the channel in that interval (including the case that there are no messages), then the available messages are sent; this does not constitute an error.
+
+h3(#rewind-example-sse). Rewind example with SSE
+
+To subscribe to a channel, getting the most recent message if available:
+
+```[javascript]
+ var querystring = 'v=1.2&channels={{RANDOM_CHANNEL_NAME}}&rewind=1&key={{API_KEY}}';
+ var eventSource = new EventSource('https://realtime.ably.io/event-stream?' + querystring);
+```
+
+h3(#rewind-examples-mqtt). Rewind example with MQTT
+
+```[nodejs]
+ var mqtt = require('mqtt');
+ var options = {
+ keepalive: 30,
+ username: '{{API_KEY_NAME}}', /* API key's name */
+ password: '{{API_KEY_SECRET}}', /* API key's secret */
+ port: 8883
+ };
+ var client = mqtt.connect('mqtts:mqtt.ably.io', options);
+ client.on('connect', () => {
+ client.subscribe('[?rewind=1]{{RANDOM_CHANNEL_NAME}}');
+ });
+ client.on('message', (topic, message) => {
+ ...
+ });
+```
+
+h2(#additional-info). Additional information
+
+At most 100 messages will be sent in a rewind request. If the number of messages within the specified interval is greater than that limit, then only the most recent messages up to that limit are sent. The attachment succeeds, but truncation of the message backlog is indicated as a non-fatal error in the attachment response.
+
+By default, a maximum of two minutes of channel history is available when attaching. This means that a rewind time specifier of greater than two minutes will only be able to rewind by two minutes. If a channel has persistence enabled, then it is possible to rewind back in time by up to the persistence TTL on the channel.
+
+The channel position expressed by a @rewind@ parameter only has an effect on an initial channel attachment. Any subsequent reattachment of the same channel on the same connection, in order to resume the connection, will attempt to resume with continuity from the point at which the connection dropped. (There are a few exceptions to this: in particular, client libraries earlier than v1.2 that have been disconnected for over two minutes, and all clients when using "@recover@ mode":/realtime/connection#connection-state-recovery ; in both cases the previous attachment state is not preserved).
+
+Any @rewind@ parameter value that cannot be parsed either as a number or a time specifier will cause the attachment request to fail and return an error.
+
+h1. API Reference
+
+inline-toc.
+ ChannelsOptions Details:
+ - ChannelOptions#channel-options
+
+h3(#channel-options).
+ default: ChannelOptions Object
+ objc,swift: ARTChannelOptions
+ java: io.ably.lib.types.ChannelOptions
+ csharp: IO.Ably.Realtime.ChannelOptions
+
+<%= partial partial_version('types/_channel_options') %>
diff --git a/content/realtime/history.textile b/content/realtime/history.textile
index 64bea8d240..343a8c8b7e 100644
--- a/content/realtime/history.textile
+++ b/content/realtime/history.textile
@@ -142,11 +142,11 @@ To enable history on a channel, it is necessary to add a channel rule in the set
h3(#continuous-history). Continuous history
-By using "rewind":/realtime/channel-params#rewind or History's @untilAttach@, it is possible to obtain message history that is continuous with the realtime messages received on an attached channel.
+By using "rewind":/realtime/channels/channel-parameters/rewind or History's @untilAttach@, it is possible to obtain message history that is continuous with the realtime messages received on an attached channel.
h4(#rewind). Rewind
-If you wish to obtain history as part of attaching to a channel, you can use the "rewind channel parameter":/realtime/channel-params#rewind. 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. Check out the main "rewind documentation":/realtime/channel-params#rewind for further details.
+If you wish to obtain history as part of attaching to a channel, you can use the "rewind channel parameter":/realtime/channels/channel-parameters/rewind. 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. Check out the main "rewind documentation":/realtime/channels/channel-parameters/rewind for further details.
In current Ably libraries, or when using the "service without a library":https://www.ably.io/adapters, this is done by qualifying the channel name. There is a future library release planned in which it will be possible to specify channel params directly via the API. As an example, if the channel were @[some_metadata]my_channel@, you would specify the use of rewind via @[some_metadata?rewind=1]my_channel@. You can specify either a number of messages up to 100 to rewind to (for example @10@), or a time specifier of either seconds (@10s@), or minutes (@2m@) up to 2 minutes.
diff --git a/content/tutorials/reactor-event-aws.textile b/content/tutorials/reactor-event-aws.textile
index 44dccade04..67e213fb6f 100644
--- a/content/tutorials/reactor-event-aws.textile
+++ b/content/tutorials/reactor-event-aws.textile
@@ -198,7 +198,7 @@ Here we're going to enter some preliminary code just to test that everything is
# Scroll down and edit @index.js@ in the _Function Code_ section. Enter the following code into the editor:
-```[js]
+```[javascript]
const AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
@@ -383,7 +383,7 @@ The application script has a few parts to it, but isn't too complex. The script
The first thing to do is to instantiate the "Ably client library":https://www.ably.io/download (already linked for you in the above HTML) and verify that we can send and receive messages successfully. The following code will define a channel, subscribe to it, and use it to publish customer messages. If everything is working, the fact that we're subscribed to the same channel that we're publishing to means that our messages will simply be echoed back to us via the @receiveMessage@ handler function, which will then append them to the chat log in the HTML.
-```[js]
+```[javascript]
// This Ably client library is available due to the script in the HTML header:
const ably = Ably.Realtime('YOUR_API_KEY_GOES_HERE');
@@ -456,7 +456,7 @@ If two different customers are trying to order a pizza at the same time, we don'
Here's some code to generate a random customer ID. This will be run when the page first loads, and you'll be able to then use the customerID in your channel names:
-```[js]
+```[javascript]
// Generate a customer id, being sure to sandbox the setup code inside a closure
const customerId = (function () {
@@ -484,7 +484,7 @@ const customerId = (function () {
Split your channel initialization code to generate two channels instead of one:
-```[js]
+```[javascript]
// The customer's messages will be posted here
const outboundChannel = ably.channels.get('pizza:customer:' + customerId);
@@ -497,7 +497,7 @@ inboundChannel.subscribe(receiveMessage);
Let's refactor the message publishing code into its own function. We'll also modify what we're sending so that it includes the customer ID in the message data. This will make it easy for the Lambda function to reply on the correct channel without having to extract the customer ID from the name of the inbound channel:
-```[js]
+```[javascript]
function postMessage(message) {
// Note the additional call to appendMessageElement, which previously was only
// being used by the receiveMessage handler. Because we want to visually
@@ -516,7 +516,7 @@ function postMessage(message) {
Don't forget to also update your @processInput@ function to use @postMessage@ rather than publishing directly:
-```[js]
+```[javascript]
function processInput(e) {
if (e.which !== 13) { // Character code 13 is the ENTER key
return;
@@ -535,7 +535,7 @@ We could test the above now, and you certainly can do so if you want to be sure
Go back and edit your Lambda function with the following code ("which you can find in the tutorial repository":https://github.com/ably/tutorials/tree/reactor-event-aws-javascript/lambda-function.js):
-```[js]
+```[javascript]
// We'll need the Node.js HTTPS module to post a response back to Ably
const https = require("https")
@@ -663,7 +663,7 @@ Also, the UI is not very useful unless the text input box is focused at all time
Modify the @appendMessage@ function to return a reference to the HTML element it has just appended to the DOM:
-```[js]
+```[javascript]
function appendMessageElement(type, message) {
const div = document.createElement('div');
div.classList.add(type, 'message');
@@ -679,7 +679,7 @@ function appendMessageElement(type, message) {
Next, we'll define a special "waiting" message that we add when the user types a message, and remove when a reply is received:
-```[js]
+```[javascript]
let waitingElement; // persist a reference to the HTML element
function setWaiting(isWaiting) { // isWaiting should be true or false
@@ -707,7 +707,7 @@ function setWaiting(isWaiting) { // isWaiting should be true or false
Update the code to call the above function when sending and receiving messages:
-```[js]
+```[javascript]
function receiveMessage(message) {
setWaiting(false); // The bot has replied, so remove the waiting message
appendMessageElement('bot', message.data);
@@ -722,7 +722,7 @@ function postMessage(message) {
Finally, at the end of the script where your text box event listener is defined, hook up a couple of extra event listeners to keep the user focused on the input text box:
-```[js]
+```[javascript]
function focusInputField() {
inputField.focus();
}
diff --git a/content/tutorials/reactor-event-azure.textile b/content/tutorials/reactor-event-azure.textile
index 825fe62ae4..102102e00d 100644
--- a/content/tutorials/reactor-event-azure.textile
+++ b/content/tutorials/reactor-event-azure.textile
@@ -126,7 +126,7 @@ In the interface displayed, you'll see a space to edit your function, and a tab
In the code editor, update the code editor with the following script, **making sure to update the line containing "YOUR_API_KEY_HERE" with your actual API key:**
-```[js]
+```[javascript]
const Ably = require('ably');
const processMessage = require('./pizza');
@@ -195,7 +195,7 @@ Next, click the "View files" tab to the far right of the Azure function code edi
When the script files are uploaded, click the "Test" option right above the file list. In the "Request body" editor, enter the following JSON code:
-```[js]
+```[javascript]
{
"customerId": "abc123",
"message": "I'd like a pepperoni pizza, please"
@@ -311,7 +311,7 @@ The first thing to do is to instantiate the "Ably client library":https://www.ab
*Don't forget to replace @YOUR_API_KEY_GOES_HERE@ with your actual API key!*
-```[js]
+```[javascript]
// This Ably client library is available due to the script in the HTML header:
const ably = Ably.Realtime('YOUR_API_KEY_GOES_HERE');
@@ -384,7 +384,7 @@ If two different customers are trying to order a pizza at the same time, we don'
Here's some code to generate a random customer ID. This will be run when the page first loads, and you'll be able to then use the customerID in your channel names:
-```[js]
+```[javascript]
// Generate a customer id, being sure to sandbox the setup code inside a closure
const customerId = (function () {
@@ -412,7 +412,7 @@ const customerId = (function () {
Split your channel initialization code to generate two channels instead of one:
-```[js]
+```[javascript]
// The customer's messages will be posted here
const outboundChannel = ably.channels.get('pizza:customer:' + customerId);
@@ -425,7 +425,7 @@ inboundChannel.subscribe(receiveMessage);
We're not using any special server-side persistence features, so we'll need to keep track of the conversation context and order details ourselves, and pass them to the chat bot as the conversation progresses. Only the bot will be updating the context; we'll just keep track of it on the bot's behalf, so that it isn't lost from one message to the next.
-```[js]
+```[javascript]
let context = null; // We start with an uninitialized conversation context
function receiveMessage(message) {
@@ -436,7 +436,7 @@ function receiveMessage(message) {
Let's refactor the message publishing code into its own function. We'll also modify what we're sending so that it includes the customer ID in the message data. This will make it easy for the Azure function to reply on the correct channel without having to extract the customer ID from the name of the inbound channel:
-```[js]
+```[javascript]
function postMessage(message) {
// Note the additional call to appendMessageElement, which previously was only
// being used by the receiveMessage handler. Because we want to visually
@@ -455,7 +455,7 @@ function postMessage(message) {
Don't forget to also update your @processInput@ function to use @postMessage@ rather than publishing directly:
-```[js]
+```[javascript]
function processInput(e) {
if (e.which !== 13) { // Character code 13 is the ENTER key
return;
@@ -480,7 +480,7 @@ Also, the UI is not very useful unless the text input box is focused at all time
Modify the @appendMessage@ function to return a reference to the HTML element it has just appended to the DOM:
-```[js]
+```[javascript]
function appendMessageElement(type, message) {
const div = document.createElement('div');
div.classList.add(type, 'message');
@@ -496,7 +496,7 @@ function appendMessageElement(type, message) {
Next, we'll define a special "waiting" message that we add when the user types a message, and remove when a reply is received:
-```[js]
+```[javascript]
let waitingElement; // persist a reference to the HTML element
function setWaiting(isWaiting) { // isWaiting should be true or false
@@ -524,7 +524,7 @@ function setWaiting(isWaiting) { // isWaiting should be true or false
Update the code to call the above function when sending and receiving messages:
-```[js]
+```[javascript]
function receiveMessage(message) {
setWaiting(false); // The bot has replied, so remove the waiting message
context = message.data.context;
@@ -540,7 +540,7 @@ function postMessage(message) {
Finally, at the end of the script where your text box event listener is defined, hook up a couple of extra event listeners to keep the user focused on the input text box:
-```[js]
+```[javascript]
function focusInputField() {
inputField.focus();
}
diff --git a/content/tutorials/reactor-event-google.textile b/content/tutorials/reactor-event-google.textile
index 09e1b8889a..71f8193707 100644
--- a/content/tutorials/reactor-event-google.textile
+++ b/content/tutorials/reactor-event-google.textile
@@ -99,7 +99,7 @@ From the "tutorial repository":https://github.com/ably/tutorials/tree/reactor-ev
Create a new file in your project folder, and name it @index.js@. Copy the following script into the file, **making sure to update the line containing "YOUR_API_KEY_HERE" with your actual API key:**
-```[js]
+```[javascript]
const Ably = require('ably');
const processMessage = require('./pizza');
@@ -184,7 +184,7 @@ When your function has been deployed successfully, it's time to test it and make
Back in the Google Cloud interface for your deployed function, you'll see a set of tabs, one of which is labelled "Testing". Enter the following JSON code into the field labelled "Triggering event", then click the blue "Test the function" button:
-```[js]
+```[javascript]
{
"customerId": "test",
"message": "I'd like a pepperoni pizza, please"
@@ -304,7 +304,7 @@ The code below should go in your @app.js@ file.
*Don't forget to replace @YOUR_API_KEY_GOES_HERE@ with your actual API key!*
-```[js]
+```[javascript]
// This Ably client library is available due to the script in the HTML header:
const ably = Ably.Realtime('YOUR_API_KEY_GOES_HERE');
@@ -377,7 +377,7 @@ If two different customers are trying to order a pizza at the same time, we don'
Here's some code to generate a random customer ID. This will be run when the page first loads, and you'll be able to then use the customerID in your channel names:
-```[js]
+```[javascript]
// Generate a customer id, being sure to sandbox the setup code inside a closure
const customerId = (function () {
@@ -405,7 +405,7 @@ const customerId = (function () {
Split your channel initialization code to generate two channels instead of one:
-```[js]
+```[javascript]
// The customer's messages will be posted here
const outboundChannel = ably.channels.get('pizza:customer:' + customerId);
@@ -418,7 +418,7 @@ inboundChannel.subscribe(receiveMessage);
We're not using any special server-side persistence features, so we'll need to keep track of the conversation context and order details ourselves, and pass them to the chat bot as the conversation progresses. Only the bot will be updating the context; we'll just keep track of it on the bot's behalf, so that it isn't lost from one message to the next.
-```[js]
+```[javascript]
let context = null; // We start with an uninitialized conversation context
function receiveMessage(message) {
@@ -429,7 +429,7 @@ function receiveMessage(message) {
Let's refactor the message publishing code into its own function. We'll also modify what we're sending so that it includes the customer ID in the message data. This will make it easy for the Google function to reply on the correct channel without having to extract the customer ID from the name of the inbound channel:
-```[js]
+```[javascript]
function postMessage(message) {
// Note the additional call to appendMessageElement, which previously was only
// being used by the receiveMessage handler. Because we want to visually
@@ -448,7 +448,7 @@ function postMessage(message) {
Don't forget to also update your @processInput@ function to use @postMessage@ rather than publishing directly:
-```[js]
+```[javascript]
function processInput(e) {
if (e.which !== 13) { // Character code 13 is the ENTER key
return;
@@ -473,7 +473,7 @@ Also, the UI is not very useful unless the text input box is focused at all time
Modify the @appendMessage@ function to return a reference to the HTML element it has just appended to the DOM:
-```[js]
+```[javascript]
function appendMessageElement(type, message) {
const div = document.createElement('div');
div.classList.add(type, 'message');
@@ -489,7 +489,7 @@ function appendMessageElement(type, message) {
Next, we'll define a special "waiting" message that we add when the user types a message, and remove when a reply is received:
-```[js]
+```[javascript]
let waitingElement; // persist a reference to the HTML element
function setWaiting(isWaiting) { // isWaiting should be true or false
@@ -517,7 +517,7 @@ function setWaiting(isWaiting) { // isWaiting should be true or false
Update the code to call the above function when sending and receiving messages:
-```[js]
+```[javascript]
function receiveMessage(message) {
setWaiting(false); // The bot has replied, so remove the waiting message
context = message.data.context;
@@ -533,7 +533,7 @@ function postMessage(message) {
Finally, at the end of the script where your text box event listener is defined, hook up a couple of extra event listeners to keep the user focused on the input text box:
-```[js]
+```[javascript]
function focusInputField() {
inputField.focus();
}
diff --git a/data/jsbins.yaml b/data/jsbins.yaml
index 5c14ebb605..0fd3784b7e 100644
--- a/data/jsbins.yaml
+++ b/data/jsbins.yaml
@@ -31,7 +31,6 @@ jsbin_hash:
"+7edQ0/+RVdXbSaumOltjdMtYW8=": udenus
J6B4SjIgGXie5DHoSF30sGPzb64=: akutul
dqNcZL3zwOeS3PmMAzJ54wVDkNg=: utizit
- fDq9P84O672WgwAsIhYmPxnPLRA=: azifaq
1vHSWAcT6EJzqWwQU4oqY1onx1o=: akimez
eBxLJU1YqdX+JW9JQY70S4iQfmU=: omedab
pzHKfCW4/o2wDnCfbg1m0Pkl7v8=: ovucin
@@ -43,6 +42,9 @@ jsbin_hash:
Sf9QPfpwxOvNcKy+KTQKXHq8dbc=: umikub
6voT8sjZ4GnNONoP3sW/Uaq2GkE=: aqalul
5SY9KnghzlKVnF/NlXih5jySnug=: utulow
+ UjxZZTfXMcghYm5u6mmtFfDVdHc=: iyoqim
+ Mxz+xzHdWwWQBNl+Nkhr/yalpmg=: ajosuv
+ mZcTN8McM4HXm+nDeiZqLj9gq8w=: izopuj
jsbin_id:
adapters/pusher-pub-sub: eBxLJU1YqdX+JW9JQY70S4iQfmU=
adapters/pubnub-pub-sub: pzHKfCW4/o2wDnCfbg1m0Pkl7v8=
@@ -84,6 +86,8 @@ jsbin_id:
sse/sse: 5SY9KnghzlKVnF/NlXih5jySnug=
sse/eventstream: 6voT8sjZ4GnNONoP3sW/Uaq2GkE=
rest/batch-presence: cHvxfaSCnHR6qzdutJZ/TZWK0FE=
- realtime/rewind: fDq9P84O672WgwAsIhYmPxnPLRA=
+ realtime/rewind: Mxz+xzHdWwWQBNl+Nkhr/yalpmg=
authentication/jwt-token: Q0XIw74KKfK3Zv34fqKSck4ip5U=
realtime/connection-recover: Sf9QPfpwxOvNcKy+KTQKXHq8dbc=
+ realtime/channel-deltas: UjxZZTfXMcghYm5u6mmtFfDVdHc=
+ realtime/channel-deltas-size: mZcTN8McM4HXm+nDeiZqLj9gq8w=