sdk-for-flutter icon indicating copy to clipboard operation
sdk-for-flutter copied to clipboard

🐛 Bug Report: Subscribing Multiple Times with a Single Realtime Instance Results in Null Subscription

Open stnguyen90 opened this issue 1 year ago • 14 comments

👟 Reproduction steps

  1. Instantiate a Realtime instance
  2. Subscribe to a channel
  3. Subscribe to another channel

👍 Expected behavior

You get 2 separate working subscriptions

👎 Actual Behavior

log:

subscription: null

The subscriptions don't receive any events.

It looks like the 2nd subscribe closes the websocket, which triggers:

https://github.com/appwrite/sdk-for-flutter/blob/54f30faa4a4fab2af0fe57c172550b288c9cbb4b/lib/src/realtime_mixin.dart#L85-L86

The _closeConnection() results in the _lastUrl being set to null:

https://github.com/appwrite/sdk-for-flutter/blob/54f30faa4a4fab2af0fe57c172550b288c9cbb4b/lib/src/realtime_mixin.dart#L27

and the _channels.clear() prevents future events from being dispatched properly:

https://github.com/appwrite/sdk-for-flutter/blob/54f30faa4a4fab2af0fe57c172550b288c9cbb4b/lib/src/realtime_mixin.dart#L71-L75

🎲 Appwrite version

Different version (specify in environment)

💻 Operating system

Linux

🧱 Your Environment

Tested with SDK version 9.0.0 and Appwrite version 1.3.4. For some reason, this only happens on mobile and not on flutter web.

👀 Have you spent some time to check if this issue has been raised before?

  • [X] I checked and didn't find similar issue

🏢 Have you read the Code of Conduct?

stnguyen90 avatar Jun 20 '23 16:06 stnguyen90

For now, the best thing to do is to use a separate Realtime instance for each subscription.

stnguyen90 avatar Jun 23 '23 18:06 stnguyen90

@stnguyen90 what is the behavior with other SDKs?

lohanidamodar avatar Jun 29 '23 01:06 lohanidamodar

@lohanidamodar, it works fine in other SDKs. You can use 1 realtime instance and make multiple subscribe calls. The problem with our Flutter SDK is how we handle reconnects.

stnguyen90 avatar Aug 03 '23 02:08 stnguyen90

Same issue with me

rvndsngwn avatar Oct 03 '23 01:10 rvndsngwn

Same here. Is there any milestone, when this will be fixed?

Schrolli91 avatar Nov 08 '23 06:11 Schrolli91

This needs addressing, really handicaps one of the most useful features.

puntiz avatar Nov 10 '23 22:11 puntiz

Same here, what is the status of this issue?

moshOntong-IT avatar Dec 05 '23 00:12 moshOntong-IT

same here. Thank you for the work.

Sokratesli avatar Dec 05 '23 09:12 Sokratesli

Hey, we have a work in progress fix for this issue, if you could test the SDK from this branch and let us know that would be great https://github.com/appwrite/sdk-for-flutter/tree/fix-realtime-multiple-subscription

dependencies:
	appwrite:
		git:
			url: https://github.com/appwrite/sdk-for-flutter
			ref: fix-realtime-multiple-subscription

lohanidamodar avatar Dec 17 '23 00:12 lohanidamodar

Hi @lohanidamodar

I have just tested the SDK from the fix-realtime-multiple-subscription branch and now multiple subscriptions are working. Your help is greatly appreciated.

rvndsngwn avatar Dec 18 '23 08:12 rvndsngwn

Hi @lohanidamodar

I have just tested the SDK from the fix-realtime-multiple-subscription branch and now multiple subscriptions are working. Your help is greatly appreciated.

Thank you for your validation. Will work to get it released soon.

lohanidamodar avatar Dec 19 '23 07:12 lohanidamodar

Hey, we have a work in progress fix for this issue, if you could test the SDK from this branch and let us know that would be great https://github.com/appwrite/sdk-for-flutter/tree/fix-realtime-multiple-subscription

dependencies:
	appwrite:
		git:
			url: https://github.com/appwrite/sdk-for-flutter
			ref: fix-realtime-multiple-subscription

I tried this patch and an error occurs when restarting the realtime stream whenever it closes

Here's the stacktrace

Concurrent modification during iteration: _Map len:2.
      #0      _CompactIterator.moveNext (dart:collection-patch/compact_hash.dart:714:7)
      #1      RealtimeMixin._createSocket.<anonymous closure> (package:appwrite/src/realtime_mixin.dart:88:49)
      #2      _rootRun (dart:async/zone.dart:1391:47)
      #3      _CustomZone.run (dart:async/zone.dart:1301:19)
      #4      _CustomZone.runGuarded (dart:async/zone.dart:1209:7)
      #5      _BufferingStreamSubscription._sendDone.sendDone (dart:async/stream_impl.dart:392:13)
      #6      _BufferingStreamSubscription._sendDone (dart:async/stream_impl.dart:402:7)
      #7      _BufferingStreamSubscription._close (dart:async/stream_impl.dart:291:7)
      #8      _ForwardingStream._handleDone (dart:async/stream_pipe.dart:99:10)
      #9      _ForwardingStreamSubscription._handleDone (dart:async/stream_pipe.dart:161:13)
      #10     _rootRun (dart:async/zone.dart:1391:47)
      #11     _CustomZone.run (dart:async/zone.dart:1301:19)
      #12     _CustomZone.runGuarded (dart:async/zone.dart:1209:7)
      #13     _BufferingStreamSubscription._sendDone.sendDone (dart:async/stream_impl.dart:392:13)
      #14     _BufferingStreamSubscription._sendDone (dart:async/stream_impl.dart:402:7)
      #15     _BufferingStreamSubscription._close (dart:async/stream_impl.dart:291:7)
      #16     _SyncStreamControllerDispatch._sendDone (dart:async/stream_controller.dart:792:19)
      #17     _StreamController._closeUnchecked (dart:async/stream_controller.dart:647:7)
      #18     _StreamController.close (dart:async/stream_controller.dart:640:5)
      #19     new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1171:19)
      #20     _rootRun (dart:async/zone.dart:1391:47)
      #21     _CustomZone.run (dart:async/zone.dart:1301:19)
      #22     _CustomZone.runGuarded (dart:async/zone.dart:1209:7)
      #23     _BufferingStreamSubscription._sendDone.sendDone (dart:async/stream_impl.dart:392:13)
      #24     _BufferingStreamSubscription._sendDone (dart:async/stream_impl.dart:402:7)
      #25     _BufferingStreamSubscription._close (dart:async/stream_impl.dart:291:7)
      #26     _SinkTransformerStreamSubscription._close (dart:async/stream_transformers.dart:87:11)
      #27     _EventSinkWrapper.close (dart:async/stream_transformers.dart:21:11)
      #28     _WebSocketProtocolTransformer.close (dart:_http/websocket_impl.dart:118:17)
      #29     _SinkTransformerStreamSubscription._handleDone (dart:async/stream_transformers.dart:132:24)
      #30     _rootRun (dart:async/zone.dart:1391:47)
      #31     _CustomZone.run (dart:async/zone.dart:1301:19)
      #32     _CustomZone.runGuarded (dart:async/zone.dart:1209:7)
      #33     _BufferingStreamSubscription._sendDone.sendDone (dart:async/stream_impl.dart:392:13)
      #34     _BufferingStreamSubscription._sendDone (dart:async/stream_impl.dart:402:7)
      #35     _BufferingStreamSubscription._close (dart:async/stream_impl.dart:291:7)
      #36     _SyncStreamControllerDispatch._sendDone (dart:async/stream_controller.dart:792:19)
      #37     _StreamController._closeUnchecked (dart:async/stream_controller.dart:647:7)
      #38     _StreamController.close (dart:async/stream_controller.dart:640:5)
      #39     _Socket._onData (dart:io-patch/socket_patch.dart:2454:21)
      #40     _rootRunUnary (dart:async/zone.dart:1407:47)
      #41     _CustomZone.runUnary (dart:async/zone.dart:1308:19)
      #42     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7)
      #43     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
      #44     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
      #45     _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19)
      #46     _StreamController._add (dart:async/stream_controller.dart:658:7)
      #47     _StreamController.add (dart:async/stream_controller.dart:606:5)
      #48     _RawSecureSocket._closeHandler (dart:io/secure_socket.dart:899:21)
      #49     _RawSecureSocket._tryFilter (dart:io/secure_socket.dart:1029:11)
      <asynchronous suspension>

Andree98 avatar Dec 29 '23 15:12 Andree98

@Andree98 can you share code to reproduce this error

lohanidamodar avatar Jan 02 '24 01:01 lohanidamodar

@lohanidamodar I'm able to reproduce the stacktrace with this code

@override
void initState() {
  super.initState();

  final realtime = Realtime(clientInstance);

  final subscription1 = realtime.subscribe([channel1]);
  final subscription2 = realtime.subscribe([channel2]);

  subscription1.stream.listen(print);
  subscription2.stream.listen(print);
}

Run the app and wait for the subscription to be killed. For me this takes 2-3 minutes using Cloudflare proxy. when the subscriptions are killed the stacktrace will appear in the console

Andree98 avatar Jan 02 '24 07:01 Andree98

@lohanidamodar Same issue with me, is the work in progress? Thanks

SafakB avatar Apr 12 '24 08:04 SafakB

Latest release should resolve this issue.

lohanidamodar avatar Apr 25 '24 02:04 lohanidamodar

Latest release should resolve this issue.

Hello and thank you for your help! Do you mean 12.0.3 ? Or next release ? Because I have the same issue on 12.0.3 :(

KiloNovemberDelta avatar Apr 28 '24 17:04 KiloNovemberDelta