A question about more about coroutines and cancell...
# apollo-kotlin
s
A question about more about coroutines and cancellations in combination with Apollo. More in thread đŸ§”
*Note, everything below is on apollo-kotlin v2.0 I have some convenience functions to turn my queries into a result type:
Copy code
sealed class QueryResult<out T> {
    data class Success<T>(val data: T) : QueryResult<T>()
    sealed class Error : QueryResult<Nothing>() {
        data class NoDataError() : Error()
        data class GeneralError() : Error()
        data class QueryError() : Error()
        data class NetworkError() : Error()
    }
   ... toOption(), toEither, etc convenience functions to turn into arrow.core types
}

fun <T> Response<T>.toQueryResult(): QueryResult<T> {
    val data = data
    return when {
        hasErrors() -> QueryResult.Error.QueryError()
        data != null -> QueryResult.Success(data)
        else -> QueryResult.Error.NoDataError()
    }
}

suspend fun <T> ApolloCall<T>.safeQuery(): QueryResult<T> {
    return try {
        await().toQueryResult()
    } catch (apolloException: ApolloException) {
        QueryResult.Error.NetworkError()
    } catch (throwable: Throwable) {
        QueryResult.Error.GeneralError()
    }
}
But my question then comes, am I breaking cancellation handling this way? I can see that
.await()
is a
suspendCancellableCoroutine
, but I am then wrapping it in a try which catches
CancellationException
too, since I am catching
Throwable
. Would this function in this way if the scope gets cancelled: 1. The flow/await cancels, and the query is stopped properly 2. The CancellationException is caught by my function 3. If anything is called under await() it does continue normally even though we were cancelled since I “swallowed” the exception there. (What I am trying to avoid) 4. On the next cancellation cooperating function we will actually exit since the job is in “cancelling” state. (Does this step actually happen? Or does the exception get swallowed completely which is even worse?) And would maybe something like adding this in both functions this fix this problem? (Or perhaps in a different order of catching if it’d matter?)
Copy code
try {
    await().toQueryResult()
} catch (apolloException: ApolloException) {
    QueryResult.Error.NetworkError(apolloException.localizedMessage)
} catch(cancelationException: CancelationException) { 
    throw cancelationException
} catch (throwable: Throwable) {
    QueryResult.Error.GeneralError(throwable.localizedMessage)
}
Or am I just tripping and none of this happens? Ugh cancellation in Coroutines sometimes feels so tricky, every time I think I get it, I realize I don’t 😅 Sorry for asking such a big question, the thing is I find it very hard to setup a test for all these scenarios with my limited coroutines understanding while involving apollo-kotlin too. I’d rather ask if someone knows before I spend way too much time trying to understand it and potentially fail.
w
I haven’t dived deep here, but
But my question then comes, am I breaking cancellation handling this way?
Probably yes, or if not cancellation then structured concurrency in some way. Even with Kotlin’s own
runCatching
you should always rethrow
CancellationException
(https://github.com/Kotlin/kotlinx.coroutines/issues/1814)
🙏 1
👍 1
s
Right, that’s what I wanted to say, structured concurrency, not cancellation. So since it is that way, would doing my extra catch clause and rethrowing of the
CancellationException
be the fix? The linked issue seems to point to “yes” right?
b
I’m not a structured concurrency/cancellation expert either but that seems right to me 🙂
➕ 1
s
Awesome, will give it a go and see how it works, thanks for the help on this one, I think the more I think about this topic alone the more likely I am to go insane 😅
😁 1
w
I think you may get some more guidance on #coroutines btw (or perhaps there’s already some past discussion about it!)
e
catching everything is not a good idea. yes, that breaks coroutine cancellation, you needed to allow CancellationException through, but it also puts you in a strange state w.r.t. Java's thread interrupt, if you swallow InterruptedException but continue execution you'll find mysteriously failing operations. not that you should be using Thread.interrupt with coroutines, but an additional warning against catching Throwable
today i learned 1
👀 1