I recently spotted a case I can't fully understand while working with Monix Task
:
There are two functions (in queue msg handler):
def handle(msg: RollbackMsg): Task[Unit] = {
logger.info(s"Attempting to rollback transaction ${msg.lockId}")
Task.defer(doRollback(msg)).onErrorRestart(5).foreachL { _ =>
logger.info(s"Transaction ${msg.lockId} rolled back")
}
}
private def doRollback(msg: RollbackMsg): Task[Unit] =
(for {
originalLock <- findOrigLock(msg.lockId)
existingClearanceOpt <- findExistingClearance(originalLock)
_ <- clearLock(originalLock, existingClearanceOpt)
} yield ()).transact(xa)
The internals of doRollback
's for-comprehension are all a set of doobie calls returning ConnectionIO[_]
monad and then transact
is run on it turning the composition into Monix Task
.
Now, as seen in handle
function I'd like entire process to retry 5 times in case of failure. The mysterious part is that this simple call:
doRollback(msg).onErrorRestart(5)
doesn't really restart the operation on exception (verified in tests). In order to get this retry behaviour I have to explicitly wrap it in Task.defer
, or have it already within Task
"context" in any other way.
And this is the point I don't fully get: why is it so? doRollback
already gives me Task
instance, so I should be able to call onErrorRestart
on it, no? If it's not the case how can I be sure that a Task
instance i get from "somewhere" is ok to be restarted or not?
What am I missing here?