supabase / supabase-flutter

Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
https://supabase.com/
MIT License
735 stars 183 forks source link

Rejoin doesn't re-add the channel to RealtimeClient #568

Closed markszente closed 1 year ago

markszente commented 1 year ago

Describe the bug In case RealtimeClient gets disconnected and subsequently reconnects, channels do not get re-added to the client.

To Reproduce

  1. Create a channel: Supabase.instance.client.channel(...)
  2. Make the device disconnect, eg. turn off wifi.
  3. Re-connect device
  4. Observe that the client reconnects; onConnMessage gets called from the server.
  5. Observe that rejoinUntilConnected on RealtimeChannel keeps calling rejoin indefinitely, however, the channel never actually get-added to the client.

Expected behavior Channels should be re-added once the client has reconnected

Version (please complete the following information): Latest from main.

Additional information It appears that only RealtimeClient.channel adds the channel to the client. However, when connection is closed, the channel removes itself from the client (socket), see: socket.remove(this);

dshukertjr commented 1 year ago

Thanks for finding this @markszente. Will take a look and try to fix it ASAP

dshukertjr commented 1 year ago

@markszente Could you see if you can reproduce the issue now? I used to be able to when you opened this issue, and as I was trying to find the cause, it stopped happening for me.

markszente commented 1 year ago

@dshukertjr Yeah, it's still reproducible. The code is fairly complex, so I cannot pinpoint, nor fix, the exact issue. Essentially, however, on a very high level, the problem is that whenever the client disconnects, the channel gets removed from it, and when connection is restored, the channel doesn't get the messages anymore.

dshukertjr commented 1 year ago

@markszente Thanks for letting me know that it is still reproducible. Would you be able to share the project ref that you are trying it on? You can send it via Twitter DM if you don't feel comfortable writing it here.

markszente commented 1 year ago

@dshukertjr Sure. I've been testing on a very small project I made specifically to identify this issue.

see the code here ``` import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; void main() async { await Supabase.initialize( url: anonKey: ); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { String? name; @override void initState() { final channel = Supabase.instance.client.channel('realtime:public:test').on( RealtimeListenTypes.postgresChanges, ChannelFilter( event: 'UPDATE', schema: 'public', table: 'test', filter: 'id=eq.4'), (payload, [ref]) async { setState(() { name = payload['new']['name']; }); print('payload: $payload'); print('ref: $ref'); }, ); channel.onClose(() async { print('channel closed'); }); channel.onError((e) { print('channel error: $e'); }); channel.subscribe((s, [_]) { print(s); }); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'You have pushed the button this many times:', ), Text( name ?? 'No name', style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), ); } } ```

Here is the output of the app:

flutter: SUBSCRIBED
// device disconnected here
flutter: channel error: heartbeat timeout
flutter: CHANNEL_ERROR
flutter: channel closed
flutter: CLOSED
flutter: TIMED_OUT
// device reconnected here
// nothing* here, any updates to the record in database, or any connection event, will not appear anymore 

You'd need to use a breakpoint at https://github.com/supabase/supabase-flutter/blob/a28de3377cd5dcd1e176ffbe7de68bf23cd50cfd/packages/realtime_client/lib/src/realtime_channel.dart#L181 to see it keeps getting called indefinitely, and https://github.com/supabase/supabase-flutter/blob/a28de3377cd5dcd1e176ffbe7de68bf23cd50cfd/packages/realtime_client/lib/src/realtime_client.dart#L293 to see connection is working fine after reconnect.

*Note: you may see an error referenced in #519, however, I don't think it's strictly related to this issue

dshukertjr commented 1 year ago

@markszente Thanks for the additional info. Would you be able to share your project ref of your Supabase project too? It looks something like mdembiczgqmbdobqwitc, and you can find it in your Supabase URL.

markszente commented 1 year ago

Sorry @dshukertjr, so far I've been trying to reproduce this with a local Supabase instance. Should have mentioned that. Trying with a real project, it indeed seem to work now.

dshukertjr commented 1 year ago

@markszente Awesome to hear that! It must have been one of the fixes that went live on the backend. The CLI version should get the new update soon as well!

VittorioParagallo commented 1 year ago

Same problem here

dshukertjr commented 1 year ago

@VittorioParagallo Would you be able to provide your Supabase ref so that I could reproduce the issue?