@@ -753,124 +753,47 @@ channel.Publish(context.Background(), "action", "boom!")
753
753
754
754
h3(#idempotency). Idempotent publish
755
755
756
- Ably supports idempotent publishing through its REST and realtime interfaces, ensuring that duplicate messages are not processed or forwarded .
756
+ Idempotent publishing ensures that multiple attempts to publish the same message do not result in duplicate messages. This feature is essential when network issues may cause a client to reattempt message publication without knowing if the initial attempt succeeded .
757
757
758
- Idempotent publishing automatically assigns each message a unique ID, which prevents further processing if the same message is published again, recognizing it as a duplicate. This is essential for handling retries in poor network conditions.
758
+ h4. Automatic idempotency
759
759
760
- In certain instances, idempotent publishing requires a client-supplied ID. This is particularly useful when it is impractical to maintain a single, continuously active client instance, such as during a publisher restart or when maintaining idempotency across an entire message processing pipeline involving an upstream system using message IDs. Specifying a custom message ID for REST and realtime publishing in these scenarios ensures idempotency.
760
+ For versions 1.2 and later of the Ably library, idempotent publishing is enabled by default. This means the Ably SDK automatically assigns a unique ID to each message to prevent duplicates during retries. This functionality applies to both REST and realtime publishing.
761
+ For older versions (before 1.2), this feature was not enabled by default for publishing. However, it could be activated using @idempotentRestPublishing@ in "@ClientOptions@":/api/rest-sdk#client-optionsidempotentRestPublishing.
761
762
762
- <aside data-type='note'>
763
- <p>From version 1.2, idempotent publishing is enabled by default for both REST and realtime interfaces. In earlier versions, enabling idempotent REST and realtime publishing was not the default and required activation using the @idempotentRestPublishing@ option in "@ClientOptions@.":/api/rest-sdk#client-options</p>
764
- </aside>
763
+ h4. Client ID idempotency
765
764
766
- The following example manually specifies a message ID:
765
+ There are scenarios where specifying a client-supplied ID to achieve idempotency are necessary, such as :
767
766
768
- ```[realtime_javascript]
769
- const realtime = new Ably.Realtime = '{{API_KEY}}';
770
- const channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}');
771
- const message = [{ data: 'payload', id: 'unique123' }];
767
+ * Ensuring idempotency when a publisher instance might be restarted, and continuous activity cannot be guaranteed.
768
+ * Integrating with an upstream system that uses its message IDs, ensuring idempotency across an entire message processing pipeline.
772
769
773
- try {
774
- await channel.publish(message);
775
- console.log('Message published successfully');
776
- } catch (err) {
777
- console.log('Failed to publish message:', err.message);
778
- }
779
- ```
780
-
781
- ```[rest_javascript]
782
- const rest = new Ably.Rest = '{{API_KEY}}';
783
- const channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}');
784
- const message = [{ data: 'payload', id: 'unique123' }];
770
+ In these cases, you can manually specify the message ID for REST and realtime publishing.
785
771
786
- try {
787
- await channel.publish(message);
788
- console.log('Message published successfully');
789
- } catch (err) {
790
- console.log('Failed to publish message:', err.message);
791
- }
792
- ```
772
+ The following example manually specifies a message ID:
793
773
794
- ```[realtime_nodejs]
795
- const realtime = new Ably.Realtime = '{{API_KEY}}';
796
- const channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}');
797
- const message = [{ data: 'payload', id: 'unique123' }];
798
774
799
- try {
800
- await channel.publish(message);
801
- console.log('Message published successfully');
802
- } catch (err) {
803
- console.log('Failed to publish message:', err.message);
804
- }
775
+ ```[realtime_javascript]
776
+ const realtime = new Ably.Realtime = '{{API_KEY}}';
777
+ const channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}');
778
+ const message = [{ data: 'payload', id: 'unique123' }];
805
779
```
806
780
807
- ```[rest_nodejs]
808
- const rest = new Ably.Rest = '{{API_KEY}}';
809
- const channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}');
810
- const message = [{ data: 'payload', id: 'unique123' }];
811
-
812
- try {
813
- await channel.publish(message);
814
- console.log('Message published successfully');
815
- } catch (err) {
816
- console.log('Failed to publish message:', err.message);
817
- }
781
+ ```[realtime_nodejs]
782
+ const realtime = new Ably.Realtime = '{{API_KEY}}';
783
+ const channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}');
784
+ const message = [{ data: 'payload', id: 'unique123' }];
818
785
```
819
786
820
787
```[realtime_ruby]
821
788
realtime = Ably::Realtime.new(key: '{{API_KEY}}')
822
789
channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}')
823
-
824
- begin
825
- channel.publish(name: 'example', data: 'payload', id: 'unique123')
826
- puts 'Realtime message published successfully'
827
- rescue => e
828
- puts "Failed to publish realtime message: #{e.message}"
829
- end
830
- ```
831
-
832
- ```[rest_ruby]
833
- rest = Ably::Rest.new(key: '{{API_KEY}}')
834
- channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}')
835
-
836
- begin
837
- channel.publish(name: 'example', data: 'payload', id: 'unique123')
838
- puts 'REST message published successfully'
839
- rescue => e
840
- puts "Failed to publish REST message: #{e.message}"
841
- end
790
+ channel.publish(name: 'example', data: 'payload', id: 'unique123')
842
791
```
843
792
844
793
```[realtime_python]
845
794
realtime = AblyRealtime('{{API_KEY}}')
846
795
channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}')
847
796
await channel.publish([{data: 'payload', id: 'unique123'}])
848
-
849
- def message_handler(message):
850
- if message.client_id in processed_messages:
851
- print("Duplicate message received.")
852
- else:
853
- print(f"Processing message: {message.data}")
854
- processed_messages.add(message.client_id)
855
- ```
856
-
857
- ```[rest_python]
858
- rest = AblyRest('{{API_KEY}}')
859
- channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}')
860
- await channel.publish([{data: 'payload', id: 'unique123'}])
861
-
862
- def message_handler(message):
863
- if message.client_id in processed_messages:
864
- print("Duplicate message received.")
865
- else:
866
- print(f"Processing message: {message.data}")
867
- processed_messages.add(message.client_id)
868
- ```
869
-
870
- ```[rest_PHP]
871
- $rest = new Ably\AblyRest('{{API_KEY}}');
872
- $channel = $rest->channels->get('{{RANDOM_CHANNEL_NAME}}')
873
- $channel->publish([{data: 'payload', id: 'unique123'}]);
874
797
```
875
798
876
799
```[realtime_java]
@@ -881,120 +804,31 @@ Channel channel = ably.channels.get('{{RANDOM_CHANNEL_NAME}}');
881
804
Message message = new Message();
882
805
message.data = "payload";
883
806
message.id = "unique123";
884
-
885
- channel.subscribe(msg -> {
886
- if (processedMessages.contains(msg.id)) {
887
- System.out.println("Duplicate message received.");
888
- } else {
889
- System.out.println("Processing message: " + msg.data);
890
- processedMessages.add(msg.id);
891
- }
892
- });
893
- ```
894
-
895
- ```[rest_java]
896
- ClientOptions options = new ClientOptions('{{API_KEY}}');
897
- AblyRest ably = new AblyRest(options);
898
- Channel channel = ably.channels.get('{{RANDOM_CHANNEL_NAME}}');
899
-
900
- Message message = new Message();
901
- message.data = "payload";
902
- message.id = "unique123";
903
-
904
- channel.subscribe(msg -> {
905
- if (processedMessages.contains(msg.id)) {
906
- System.out.println("Duplicate message received.");
907
- } else {
908
- System.out.println("Processing message: " + msg.data);
909
- processedMessages.add(msg.id);
910
- }
911
- });
912
807
```
913
808
914
809
```[realtime_csharp]
915
810
ARTRealtime *realtime = [[ARTRealtime alloc] initWithKey:@"{{API_KEY}}"];
916
811
ARTRealtimeChannel *channel = [realtime.channels get:@"{{RANDOM_CHANNEL_NAME}}"];
917
-
918
- [channel publish:@"example" data:@"payload" id:@"unique123" callback:^(ARTErrorInfo *error) {
919
- if (error) {
920
- NSLog(@"Error publishing message: %@", error.message);
921
- } else {
922
- NSLog(@"Message published successfully");
923
- }
924
- }];
925
- ```
926
-
927
- ```[rest_csharp]
928
- ARTRealtime *rest = [[ARTRealtime alloc] initWithKey:@"{{API_KEY}}"];
929
- ARTRealtimeChannel *channel = [rest.channels get:@"{{RANDOM_CHANNEL_NAME}}"];
930
-
931
- [channel publish:@"example" data:@"payload" id:@"unique123" callback:^(ARTErrorInfo *error) {
932
- if (error) {
933
- NSLog(@"Error publishing message: %@", error.message);
934
- } else {
935
- NSLog(@"Message published successfully");
936
- }
937
- }];
812
+ channel publish:@"example" data:@"payload" id:@"unique123" callback:^(ARTErrorInfo *error)
938
813
```
939
814
940
815
```[realtime_swift]
941
816
let realtime = ARTRealtime(key: "{{API_KEY}}")
942
817
let channel = realtime.channels.get("{{RANDOM_CHANNEL_NAME}}")
943
-
944
- channel.publish("example", data: "message data", id: "unique123") { error in
945
- if let error = error {
946
- print("Error publishing message: \(error)")
947
- } else {
948
- print("Message published successfully")
949
- }
950
- }
818
+ channel.publish("example", data: "message data", id: "unique123")
951
819
```
952
820
953
- ```[rest_swift]
954
- let rest = ARTRest(key: "{{API_KEY}}")
955
- var channel = rest.channels.get("{{RANDOM_CHANNEL_NAME}}")
956
-
957
- channel.publish([ARTMessage(name: "example", data: "message data", id: "unique123")]) { error in
958
- if let error = error {
959
- print("Error publishing message: \(error)")
960
- } else {
961
- print("Message published successfully")
962
- }
963
- }
821
+ ```[realtime_objc]
822
+ ARTRealtime *realtime = [[ARTRealtime alloc] initWithKey:("{{API_KEY}}"));
823
+ ARTRealtimeChannel *channel = [realtime.channels get:("{{RANDOM_CHANNEL_NAME}}");
824
+ [channel.publish("example", data: "message data", id: "unique123")];
964
825
```
965
826
966
827
```[realtime_flutter]
967
- final channel = _realtime.channels.get('{{RANDOM_CHANNEL_NAME}}');
968
- await channel.publish(name: 'example', data: 'message data');
969
-
970
- Future<void> initializeAndPublish(String apiKey, String channelName, String eventName, String data) async {
971
- if (_realtime == null) {
972
- _realtime = ably.Realtime(key: apiKey);
973
- }
974
-
975
- if (_channel == null || _channel.name != channelName) {
976
- _channel = _realtime.channels.get(channelName);
977
- }
978
-
979
- await _channel.publish(name: eventName, data: data);
980
- }
981
- ```
982
-
983
- ```[rest_flutter]
984
- final channel = _rest.channels.get('{{RANDOM_CHANNEL_NAME}}');
985
- await channel.publish(name: 'example', data: 'message data');
986
-
987
- Future<void> initializeAndPublish(String apiKey, String channelName, String eventName, String data) async {
988
- if (_rest == null) {
989
- _rest = ably.Realtime(key: apiKey);
990
- }
991
-
992
- if (_channel == null || _channel.name != channelName) {
993
- _channel = _realtime.channels.get(channelName);
994
- }
995
-
996
- await _channel.publish(name: eventName, data: data);
997
- }
828
+ final clientOptions = ably.ClientOptions(key: '{{API_KEY}}');
829
+ final rest = ably.Realtime(options: clientOptions);
830
+ final channel = realtime.channels.get('{{RANDOM_CHANNEL_NAME}}');
831
+ final message = ably.Message(data: 'payload', id: 'unique123');
998
832
```
999
833
1000
834
```[realtime_go]
@@ -1010,11 +844,71 @@ message := &ably.Message{
1010
844
Data: "payload",
1011
845
ID: "unique123",
1012
846
}
847
+ ```
1013
848
1014
- err = channel.Publish(context.Background(), "eventName", message)
1015
- if err != nil {
1016
- log.Fatalf("Error publishing message: %v", err)
1017
- }
849
+ ```[rest_javascript]
850
+ const rest = new Ably.Rest = '{{API_KEY}}';
851
+ const channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}');
852
+ const message = [{ data: 'payload', id: 'unique123' }];
853
+ ```
854
+
855
+ ```[rest_nodejs]
856
+ const rest = new Ably.Rest = '{{API_KEY}}';
857
+ const channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}');
858
+ const message = [{ data: 'payload', id: 'unique123' }];
859
+ ```
860
+
861
+ ```[rest_ruby]
862
+ rest = Ably::Rest.new(key: '{{API_KEY}}')
863
+ channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}')
864
+ channel.publish(name: 'example', data: 'payload', id: 'unique123')
865
+ ```
866
+
867
+ ```[rest_python]
868
+ rest = AblyRest('{{API_KEY}}')
869
+ channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}')
870
+ await channel.publish([{data: 'payload', id: 'unique123'}])
871
+ ```
872
+
873
+ ```[rest_PHP]
874
+ $rest = new Ably\AblyRest('{{API_KEY}}');
875
+ $channel = $rest->channels->get('{{RANDOM_CHANNEL_NAME}}')
876
+ $channel->publish([{data: 'payload', id: 'unique123'}]);
877
+ ```
878
+
879
+ ```[rest_java]
880
+ ClientOptions options = new ClientOptions('{{API_KEY}}');
881
+ AblyRest ably = new AblyRest(options);
882
+ Channel channel = ably.channels.get('{{RANDOM_CHANNEL_NAME}}');
883
+
884
+ Message message = new Message();
885
+ message.data = "payload";
886
+ message.id = "unique123";
887
+ ```
888
+
889
+ ```[rest_csharp]
890
+ ARTRealtime *rest = [[ARTRealtime alloc] initWithKey:@"{{API_KEY}}"];
891
+ ARTRealtimeChannel *channel = [rest.channels get:@"{{RANDOM_CHANNEL_NAME}}"];
892
+ channel publish:@"example" data:@"payload" id:@"unique123" callback:^(ARTErrorInfo *error)
893
+ ```
894
+
895
+ ```[rest_swift]
896
+ let rest = ARTRest(key: "{{API_KEY}}")
897
+ var channel = rest.channels.get("{{RANDOM_CHANNEL_NAME}}")
898
+ channel.publish([ARTMessage(name: "example", data: "message data", id: "unique123")])
899
+ ```
900
+
901
+ ```[rest_objc]
902
+ ARTRest *rest = [[ARTRest alloc] initWithKey:("{{API_KEY}}"));
903
+ ARTRestChannel *channel = [rest.channels get:("{{RANDOM_CHANNEL_NAME}}");
904
+ [channel.publish("example", data: "message data", id: "unique123")];
905
+ ```
906
+
907
+ ```[rest_flutter]
908
+ final clientOptions = ably.ClientOptions(key: '{{API_KEY}}');
909
+ final rest = ably.Rest(options: clientOptions);
910
+ final channel = rest.channels.get('{{RANDOM_CHANNEL_NAME}}');
911
+ final message = ably.Message(data: 'payload', id: 'unique123');
1018
912
```
1019
913
1020
914
```[rest_go]
@@ -1030,14 +924,11 @@ message := &ably.Message{
1030
924
Data: "payload",
1031
925
ID: "unique123",
1032
926
}
1033
-
1034
- err = channel.Publish(context.Background(), "eventName", message)
1035
- if err != nil {
1036
- log.Fatalf("Error publishing message: %v", err)
1037
- }
1038
927
```
1039
928
1040
- If manually specifying message IDs, it is important to be aware of how messages are published when calling the "publish()":/api/rest-sdk/channels#publish method with an array of messages. See this "FAQ":https://faqs.ably.com/client-specified-message-id-restrictions-for-multiple-messages-published-atomically for further information.
929
+ h4. Restrictions and considerations
930
+
931
+ When specifying custom message IDs, particularly when attempting to "publish()":/api/rest-sdk/channels#publish multiple messages atomically with idempotency, ensure that the IDs adhere to the format restrictions. For more details on these restrictions, refer to the "FAQ":https://faqs.ably.com/client-specified-message-id-restrictions-for-multiple-messages-published-atomically.
1041
932
1042
933
h3(#publish-on-behalf). Use the REST interface to publish on behalf of a realtime connection
1043
934
0 commit comments