title: Retrying Monix Task - why Task.defer is required here?
body: <p>I recently spotted a case I can't fully understand while working with Monix <code>Task</code>:</p>
  
  <p>There are two functions (in queue msg handler):</p>
  
  
  
  <pre><code>  def handle(msg: RollbackMsg): Task[Unit] = {
      logger.info(s"Attempting to rollback transaction ${msg.lockId}")
      Task.defer(doRollback(msg)).onErrorRestart(5).foreachL { _ =&gt;
        logger.info(s"Transaction ${msg.lockId} rolled back")
      }
    }
  
    private def doRollback(msg: RollbackMsg): Task[Unit] =
      (for {
        originalLock         &lt;- findOrigLock(msg.lockId)
        existingClearanceOpt &lt;- findExistingClearance(originalLock)
        _                    &lt;- clearLock(originalLock, existingClearanceOpt)
      } yield ()).transact(xa)
  </code></pre>
  
  <p>The internals of <code>doRollback</code>'s for-comprehension are all a set of <a href="https://tpolecat.github.io/doobie/" rel="nofollow noreferrer">doobie</a> calls returning <code>ConnectionIO[_]</code> monad and then <code>transact</code> is run on it turning the composition into Monix <code>Task</code>.</p>
  
  <p>Now, as seen in <code>handle</code> function I'd like entire process to retry 5 times in case of failure. The mysterious part is that this simple call:</p>
  
  
  
  <pre><code>doRollback(msg).onErrorRestart(5)
  </code></pre>
  
  <p>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 <code>Task.defer</code>, or have it already within <code>Task</code> "context" in any other way. </p>
  
  <p>And this is the point I don't fully get: why is it so? <code>doRollback</code> already gives me <code>Task</code> instance, so I should be able to call <code>onErrorRestart</code> on it, no? If it's not the case how can I be sure that a <code>Task</code> instance i get from "somewhere" is ok to be restarted or not?</p>
  
  <p>What am I missing here?</p>