Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscription switched to DISCONNECTED #2765

Closed
Tracked by #5648
legend-egy opened this issue Nov 21, 2020 · 14 comments
Closed
Tracked by #5648

Subscription switched to DISCONNECTED #2765

legend-egy opened this issue Nov 21, 2020 · 14 comments

Comments

@legend-egy
Copy link

Summary
After subscription is opened with 1 min or so it switched to Disconnected

Background
I added one subscription into my project to listen for new events coming from graphQL server
I found out that sometimes events is not received in the app however . if i am listening to subscriptions from GraphQl Playground i am able to see the events there !!

How i was sure that this is a bug
I started to log the state for the subscription using the following listener
apolloClient.subscriptionManager.addOnStateChangeListener { fromState, toState -> }

What i found is the following states are triggered :
DISCONNECTED -> CONNECTING -> CONNECTED -> ACTIVE
and i actually receive events but
After 1 min or 2 mins it is directly changed to DISCONNECTED without going to Stopping or Stopped
and in this state i don't receive any events

Version
2.4.4

MyCode

[1] setup

 val okHttpClient = okHttpClient.newBuilder().addInterceptor { chain ->
      val request = chain.request().newBuilder().apply {
        addHeader("Authorization", storage.accessToken())
      }.build()
      chain.proceed(request)
    }.build()
    
    val subscriptionTransportFactory =
      WebSocketSubscriptionTransport.Factory(envType.socketUrl(), okHttpClient)

    val authenticationParams = hashMapOf("Authorization" to storage.accessToken(),)

    return ApolloClient.builder()
      .serverUrl(baseUrl())
      .okHttpClient(okHttpClient)
      .subscriptionConnectionParams(SubscriptionConnectionParams(authenticationParams))
      .subscriptionTransportFactory(subscriptionTransportFactory)
      .build()

[2]

suspend fun subscribeToNewsEvents() = apolloClient.subscribe(NewsEventsSubscription()).toFlow() .map { it.data!!.NewsEvents!! }

[3] i am collecting this flow in the activity

My Question
[1] why it is disconnected after being Active ?
[2] how i can know the cause of this issue or log it ?
[3] how to overcome this because this a really blocking issue preventing us from releasing our app ?

Please let me know if i am not clear for the bug

@martinbonnin
Copy link
Contributor

Hi! Thanks for the detailed report. Maybe the authorisation token is expired? Is your GraphQL playground public so I could take a look by any chance?

To answer your questions:

[1] why it is disconnected after being Active ?

I guess something closed the websocket, most likely on the server but it might be on the client too.

[2] how i can know the cause of this issue or log it ?

Can you intercept the Websocket traffic with Charles Proxy? That should give you a good idea of who's closing the connection and if there are any unusual messages being transfered.

[3] how to overcome this because this a really blocking issue preventing us from releasing our app ?

A workaround would be to use Flow.retry to reconnect automatically if the connection drops.

If it's authorisation related, you can also take a look at apolloClient.subscriptionManager.reconnect() for how to update the authorization parameters

@legend-egy
Copy link
Author

legend-egy commented Nov 21, 2020

Hi Martin ,

First of all many thanks for your quick reply.

Unfortunately the playGround is not public but i can ensure that if i open a subscription from playground and on same time
subscription the app :

in playground it is not disconnected at all and always ACTIVE , but in app it ACTIVE then DISCONNECTED .

Regarding the the token expiration it is not expired because i tried to do pull to refresh after it is DISCONNECTED . to do QUERY request and receiving response as expected.

Regarding your answer if have the following questions

1] is there is a way to Log exception when it is disconnected to find what it the issue ?
Or intercept traffic using Charles Proxy will do the trick ??

2] should i use flow.retry OR apolloClient.subscriptionManager.reconnect() if this case happened ?

3] using subscriptionHeartbeatTimeout is useful for this case or it useless ?
or what the purpose of heartBeat in subscription ?

I am just trying to overcome this issue because i want to go production and afraid to have this

@martinbonnin
Copy link
Contributor

Hi! II think using Flow.retry is the quickest workaround here to reconnect when you get disconnected. Something like:

suspend fun subscribeToNewsEvents() = apolloClient.subscribe(NewsEventsSubscription()).toFlow().retry().map { it.data!!.NewsEvents!! }

There might also be something to investigate around the heartbeat but without a way to reproduce it's going to be difficult. It might be similar to this bug https://github.com/apollographql/apollo-android/issues/2733 that I wanted to investigate. I'll post some progress there if I find anything.

@legend-egy
Copy link
Author

Great i will be waiting your investigation results 👍

@martinbonnin
Copy link
Contributor

Sounds good. Just a heads up that it might take some time since I'm not super familiar with the subscription code. Also that other issue I linked is maybe completely different so if you need a short term solution, I'd recommend the retry workaround

@legend-egy
Copy link
Author

legend-egy commented Nov 23, 2020

@martinbonnin i totally understand take your time 👍

I just want to inform you that [retry] will work if exception is thorwn other than that it will not do retry
so one short term solution is to [reconnect]

So i can use reconnect()
for example

    apolloClient.subscriptionManager.addOnStateChangeListener { fromState, toState ->
         if(toState = SubscriptionManagerState.DISCONNECTED) {
        //reconnect and passing the SubscriptionConnectionParams(authenticationParams)
      }
    }

i would like in case of disconnection try at least to connect again , how i can pass the SubscriptionConnectionParams to reconnect() method ?

Also one more question :
If i reconnected what will happen to the old subscription ?
should i unsubscribe and subscribe again or no need to do this

@akelix
Copy link

akelix commented Dec 28, 2020

I think i have faced with similar problem

  • If you loose and restore connection, subscription will not be restored.

The problem lives here here

So on any error channel will be closed, and you will never recieves notifications on client.

You have to handle errors in subscription inside flow Apollo docs

You should use retryWhen

#2823

@danielehrhardt
Copy link

So on any error channel will be closed, and you will never recieves notifications on client.

Did you solved it?

@akelix
Copy link

akelix commented Jan 28, 2021

So on any error channel will be closed, and you will never recieves notifications on client.

Did you solved it?

My temporary solution here. Rewrite toFlow extension.

@omeraslam-citrusbits
Copy link

Any update on this, I am also facing same problem.

@Jazeb
Copy link

Jazeb commented Jul 12, 2021

i am also facing this issue on https server URL but on ip it works fine. Subscription disconnects after 1 min on https url but not on IP, not sure if it might be the issue with nginx.

@martinbonnin
Copy link
Contributor

Anyone still around with this issue? It's been a while but 4.0.0-beta.5 introduces a new approach to retrying the subscriptions at the ApolloClient layer:

    ApolloClient.Builder()
        .serverUrl(sampleServer.graphqlUrl())
        .retryOnError { it.operation is Subscription }
        .subscriptionNetworkTransport(
            WebSocketNetworkTransport.Builder()
                .serverUrl(sampleServer.subscriptionsUrl())
                .build()
        )
        .build()

If anyone's get a chance to try it, I'm eager to know what you think. If not, and since this issue is almost 3 years old now, I'll close it and anyone reading this should feel free to open a new one.

@martinbonnin
Copy link
Contributor

Closing. See #5862 for follow ups.

Copy link
Contributor

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Kotlin usage and allow us to serve you better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants