what's the best practice to implement transactions...
# getting-started
s
what's the best practice to implement transactions when dealing with coroutines ? Essentially I am launching some parallel tasks and want to rollback everything in case one fails
e
I don’t have a clue but to figure it out you could c_ompose_ the logic, like lego. Figure out how you would implement roll back for just one of those operations. When you have that, extrapolating to multiple operations is more straight forward
j
I don't think coroutines can really help you there. If you have multiple tasks that interact with a DB, and one fails, how would coroutines know how to rollback? This is the responsibility of the DB-handling code. You can write a try-catch-finally block around a piece of code that does parallel decomposition (for instance around a
coroutineScope { ... }
block), and thanks to structured concurrency, you'll get exceptions bubbled up to your try/catch. You can then use the rollback facilities of whatever system you're using in your
catch
block, and the commit facilities in the
finally
.
As @efemoney said, this is no different from doing this for a single operation. Just extract all the parallel decomposition into a single
suspend
function, and then it's back to one operation that can fail. Then perform the commit/rollback as you would with one operation.
b
Tried and true [3 phase commit](https://en.wikipedia.org/wiki/Three-phase_commit_protocol#:~:text=In%20computer%20networking%20and%20databases,phase%20commit%20protocol%20(2PC).)? Have each participant have a separate "prepare" and "commit" function in addition to the "rollback". Then have a monitor invoke all the prepares and wait for all to succeed, only then invoking all the commits (and waiting for them to succeed). If any fails, invoke all the rollbacks. This question got me to read https://kotlinlang.org/docs/coroutines-basics.html#scope-builder-and-concurrency, (I'm a kotlin n00b), but I think each phase could naturally be coded as a
coroutineScope
.
1