apollographql / apollo-kotlin

:rocket:  A strongly-typed, caching GraphQL client for the JVM, Android, and Kotlin multiplatform.
https://www.apollographql.com/docs/kotlin
MIT License
3.76k stars 653 forks source link

Migrating to Apollo 3: Usage of `SubscriptionManager.start/stop` #6153

Open mformetal opened 1 month ago

mformetal commented 1 month ago

While migrating from Apollo 2 to Apollo 3, I can't find a trivial replacement for usage of ApolloClient::enableSubscriptions/disableSubscriptions. These APIs called down to SubscriptionManager.start/stop - disable* seems to have a corollary in:

client.subscriptionNetworkTransport.dispose()

This seems to close the underlying Channel - would I have to recreate the subscriptionNetworkTransport as a corollary for enable*?

Any clues/suggestions ? 🙏 Would be much appreciated!

BoD commented 1 month ago

Hi!

You are right that dispose() will stop any connection and the event loop, making the NetworkTransport not re-usable, so after calling that you'll need to instantiate a new one, and to use it you'll also need a new ApolloClient (you can call ApolloClient.newBuilder() to build upon an existing one).

That is probably the simplest way to do that, but if that's impractical there may be a way to use NetworkTransport.closeConnection(YourException), and then wait for a signal to reconnect in webSocketReopenWhen, something like (untested!):

val reconnectSignalChannel = Channel<Unit>(0)

object MyDisconnectException : Exception

val apolloClient = ApolloClient.Builder()
    .(...)
    .webSocketReopenWhen { throwable, attempt ->
        if (throwable is MyDisconnectException) {
            reconnectSignalChannel.receive() // suspends until calling reconnectSignalChannel.offer(Unit)
        }
        true
    }
    .build()
mformetal commented 1 month ago

Hi!

You are right that dispose() will stop any connection and the event loop, making the NetworkTransport not re-usable, so after calling that you'll need to instantiate a new one, and to use it you'll also need a new ApolloClient (you can call ApolloClient.newBuilder() to build upon an existing one).

That is probably the simplest way to do that, but if that's impractical there may be a way to use NetworkTransport.closeConnection(YourException), and then wait for a signal to reconnect in webSocketReopenWhen, something like (untested!):

val reconnectSignalChannel = Channel<Unit>(0)

object MyDisconnectException : Exception

val apolloClient = ApolloClient.Builder()
    .(...)
    .webSocketReopenWhen { throwable, attempt ->
        if (throwable is MyDisconnectException) {
            reconnectSignalChannel.receive() // suspends until calling reconnectSignalChannel.offer(Unit)
        }
        true
    }
    .build()

Thanks for this migration path - I'm currently migrating a bunch of old Apollo2 code, I imagine this would break a lot of the wrapper logic we have. Wrote a ticket to investigate. Appreciate you!

If you don't mind, can you keep this issue open for a minute so I can try and implement your suggestions?

BoD commented 1 month ago

Hey @mformetal, anything we can do in this issue, or is it ok to close it?

mformetal commented 1 month ago

Hey @mformetal, anything we can do in this issue, or is it ok to close it?

Sorry for the late reply, tons of things on my plate - going to start work on this migration this week, mind keeping it open if I have any questions? Promise it'll be closed by Friday!

UPDATE: Apologies for the delay but I've been sick the past few days, haven't been able to make progress on this - I'll look into completing this on our side in the next few days.