cadence-workflowtemporal-workflowuber-cadence

Is it safe to invoke a workflow activity from defer() within the workflow code?


func MyWorkflow(ctx Context) (retErr error) { 
  log := workflow.GetLogger()
  log.Info("starting my workflow")
  defer func() {
    if retErr != nil {
      DoActivityCleanupError(ctx, ..)
    } else {
      DoActivityCleanupNoError(ctx, ...)
    }
  }
  err := DoActivityA(ctx, ...)
  if err != nil {
    return err
  }
  ...
  err := DoActivityB(ctx, ...)
  if err != nil {
    return err
  }
}
  

Basically there are catchall activities, ActivityCleanupNoError and ActivityCleanupError, that we want to execute whenever the workflow exits (particularly in the error case which we don't want to repeatedly call ActivityCleanupError in all error returns.

Does this work with distributed decision making? For example, if ownership of workflow decision move from one worker to another, is it going to trigger defer on the original worker?

Bonus Question: Does the logger enforce logging only once per workflow run? Even if decisions are moved from one worker to another? Do you expect to see the log line appear in both worker's log? or is there magic behind the scene to prevent this from happening?


Solution

  • Yes it is.

    But it's quite complicated to understand why it is safe. This is how to get the conclusion:

    1. In no-sticky mode(without sticky cache), Cadence SDK will always execute workflow code to make(collect) workflow decisions, and release all the goroutines/stack. When releasing them, the defer will be executed which mean the cleanup activity code path will run -- HOWEVER, those decision will be ignored. Hence it will not impact the actual correctness.

    2. In sticky mode, if workflow is not closing, Cadence SDK will be blocking on somewhere; If the workflow is actually closing, then the defer will be executed and the decisions will be collected.

    3. When the sticky cache(goroutines/stack) is evicted, what will happen is exactly the same as 1. so it's also safe.

    Does the logger enforce logging only once per workflow run? Even if decisions are moved from one worker to another? Do you expect to see the log line appear in both worker's log? or is there magic behind the scene to prevent this from happening?

    Each log line will only appear in the worker that actually executed the code as making decision -- in other words, in non-replay mode. That's the only magic :)