realtime icon indicating copy to clipboard operation
realtime copied to clipboard

Realtime connection unable to reconnect after TIMED_OUT

Open farzd opened this issue 7 months ago • 3 comments

Bug report

  • [x] I confirm this is a bug with Supabase, not with my own application.
  • [x] I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

When the realtime connection is lost on a native iOS/Android device [using Expo], attempting to auto reconnect always results in a loop. Keeps disconnecting after a reconnect.

To Reproduce

After a successful SUBSCRIBED subscription status, minimise the mobile app and lock the phone for 3 seconds. After reverting back to the app you should get two CHANNEL_ERROR subscription statuses [If you stay in the app, supabase manages to reconnect most of the time automatically]

But if you repeat the process before this reconnection and minimise the mobile app and lock the phone for 3 seconds. Reverting back to the app results in two more CHANNEL_ERROR subscription statuses. Followed by a CLOSED. After 10 seconds this results in a TIMED_OUT. if you start the reconnection process within this 10 seconds - the subscription fluctuates between SUBSCRIBED and CLOSED in a loop

Expected behavior

After CLOSED, the user defined reconnection strategy should result in a successful SUBSCRIBED state. This works if i manually disconnect [the disconnect button at the top in the screenshot triggers a removeChannel call for the active subscription ]

Screenshots

[see status log in the black] IMG_2171

System information

  • OS: iOS / Android native mobile applications [using Expo]
  • Version of supabase-js: ^2.42.5

Additional context

The basic version of my reconnection strategy, i have tested this with various methods [like reconnectingFlag] etc etc but still failing to avoid the loop described above. I've also used a flag for channel status and to only reconnect when status is not 'joined' but the TIMED_OUT forces a CLOSED after it and i'm getting strange race conditions.

IMG_2174

 const subscriptionRef = useRef(null)
 const reconnectAttemptsRef = useRef(0)

  useEffect(() => {
    if (user_id) {
      subscribeToChannel()
    }

    return () => {
      if (subscriptionRef.current) {
        supabase.removeChannel(subscriptionRef.current)
      }
    }
  }, [user_id])

  const subscribeToChannel = () => {
    subscriptionRef.current = supabase
      .channel(`XXX:${user_id}`)
      .on(
        'postgres_changes',
        {
          event: '*',
          schema: 'public',
          table: 'table',
          filter: `user_id=eq.${user_id}`,
        },
        (payload) => {
          // Reset reconnect attempts on successful message
          reconnectAttemptsRef.current = 0;
        }
      )
      .subscribe((status) =>  {
        console.log({ status });

        if (status === "SUBSCRIBED") {
          console.log("Successfully subscribed");
          reconnectAttemptsRef.current = 0;

        } else if (status === "CLOSED") {
          console.log("Subscription closed");
          reconnectWithBackoff();
        }
      })
  }

  const reconnectWithBackoff = () => {
    if (
      reconnectAttemptsRef.current < 5
    ) {
      const delay = 3000 * Math.pow(2, reconnectAttemptsRef.current)
      console.log(`Reconnecting in ${delay}ms...`)

      setTimeout(() => {
        console.log(`Reconnect attempt ${reconnectAttemptsRef.current + 1}`)
        subscribeToChannel()
        reconnectAttemptsRef.current += 1
      }, delay)
    } else {
      console.log('Max reconnection attempts reached. Please try again later.')
    }
  }

farzd avatar Jul 08 '24 03:07 farzd