Hello :slightly_smiling_face: my brain cannot figu...
# coroutines
m
Hello 🙂 my brain cannot figure this out, I have a Flow
.onEach
running (handling rabbitMQ messages). This flow should never complete/stop, except when the whole application goes down. Therefore, I have the inside of the
.onEach
wrapped by a
try-catch
block with catching
e: Exception
. Inside this code block, I run code that includes Exposed suspended transactions. It can happen that I get expected SQL exceptions, which I also catch, but somehow I always get
Copy code
kotlinx.coroutines.JobCancellationException: Parent job is Cancelling
Caused by: org.jetbrains.exposed.exceptions.ExposedSQLException
As stated, this exception is catched though, but the flow still completes. So I can only imagine that some code (maybe Exposed code) manually cancels the parent job, right?
p
you may want to enclose your work in a
supervisorScope
as any child coroutines that fail will propagate that failure to the parent scope and cause cancellation to propagate otherwise
g
Could you show some simplified example of this? It's not really clear how you run coroutines and how catch them, for example try/catch wouldn't work if you start child async which failed, even if you catch await() I don't think supervisorScope is needed for Flow if it handled correctly
m
I tried reproducing it in isolation, but I could not without using Exposed (which would require a running database). So this snippet does not work, but it matches the actual code and what happens. I think it might have to do with code in this file: https://github.com/JetBrains/Exposed/blob/8f8499c7b6fcbb81d6e96fe2de46dac9fb9e0d7e[…]rg/jetbrains/exposed/sql/transactions/experimental/Suspended.kt The exception is thrown in the marked line (187) of the link above. Wrapping the
insertIntoDatabase
call in the snippet below in
supervisorScope { … }
did fix the problem. As I realize I am no coroutine expert, I’m not fully sure why, maybe the
TransactionScope
using the parent coroutine context as its own coroutine context means, its the
Flow
s coroutine context that is cancelled when an exception inside the
suspendedTransaction
block occurs?
p
In general, you shouldn't be catching
CancellationException
without rethrowing it as doing so will break structured concurrency. If a coroutine fails (that is, throws a non-cancellation exception) then normally the parent coroutine will fail itself and propagate its cancellation to its remaining children.
supervisorScope
handles child-failure by effectively ignoring it - meaning it will not fail itself when a child fails
m
I think in my specific use case, its not that much of a problem, but I understand. Thank you. I actually found another way to prevent my problem:
newSuspendedTransaction
takes
CoroutineContext? = null
as a first optional parameter. When I call
newSuspendedTransaction(<http://Dispatchers.IO|Dispatchers.IO>)
, it behaves identical to when I wrap the code in
supervisorScope
. So I think the problem was that the TA took the flows coroutine context as its own context, which then cancelled the whole flow on failure of the transaction