Canceling Child Workflow Execution

I’d like to ask about a parent canceling/terminating its child workflow execution.

There is parent workflow A executes a child workflow A with a timer. If the timer is triggered (timeout), the child wf is cancelled/terminated. Workflow A uses below versions:

Then, there is another parent workflow B, it executes a child workflow B with a timer. If the timer is triggered (timeout), the child wf should be cancelled/terminated. When I test it, the cwf is not cancelled although workflow B has the same code as workflow A. Workflow B uses below versions:

Below is the code snippet:

> childOptions := workflow.ChildWorkflowOptions{
> 	WorkflowID:            childWorkflowId,
> 	RetryPolicy:           retryPolicy,
> 	SearchAttributes:      searchAttributes,
> 	TaskQueue:             wf.TaskQueue,
> 	WorkflowIDReusePolicy: enums.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE,
> }
> ctx = workflow.WithChildOptions(ctx, childOptions)
> 
> childFuture := workflow.ExecuteChildWorkflow(ctx, utility.ChildWorkflowName, childWfReq)
> 
> loopCtx := workflow.WithActivityOptions(ctx, activityOptions)
> 	timerCtx, timerCancel := workflow.WithCancel(loopCtx)
> 	timer := workflow.NewTimer(timerCtx, dynamicConfig.Timer)
> 	selector := workflow.NewSelector(timerCtx)
> 	selector.AddFuture(timer, func(f workflow.Future) {
> 		err1 := f.Get(timerCtx, nil)
> 		if loopCtx.Err() != nil {
> 			logger.Error("Error occurred.", "error", loopCtx.Err())
> 		}
> 	}).AddFuture(childFuture, func(f workflow.Future) {
> 		err := childFuture.Get(ctx, &request)
> 		if err != nil {
> 			logger.Error("Error occurred getting child workflow result.", "error", err)
> 		}
> 		timerCancel()
> 	}).Select(loopCtx)
> }
  1. Could the different behaviors caused by different sdk/api versions?

I tried to modify workflow B to manually cancel the child workflow:

  • I added WaitForCancellation.
childOptions := workflow.ChildWorkflowOptions{
	...
	WaitForCancellation:   true,
}
  • I defined a cancel handler:
childCtx, cancelHandler := workflow.WithCancel(ctx)
  • Then I called the handler when the timer is triggered:
cancelHandler()

However, the child workflow is still running. I also tried to use childCtx.Done() but it does not work.

In the code snippet posted I don’t see any attempts to cancel the child.

Yes, that is the code snippet of Workflow A. I don’t specify any cancelHandler, but when timer is triggered, the child WF is automatically terminated.

When the cwf is initiated, the parent close policy is PARENT_CLOSE_POLICY_TERMINATE.

However, I never specified parentClosePolicy in the childOptions as shown in the snnipet. So I assumed it would use the default policy as stated in docs.

In Temporal WEB UI, the child WF is terminated. The reason in result section is ‘by parent close policy’.

Using the same code, I applied it to workflow B. The parent close policy is PARENT_CLOSE_POLICY_TERMINATE too.

However, after the timer is triggered, the child WF is still running.

I believe the documentation is incorrect, and PARENT_CLOSE_POLICY_TERMINATE is the default.

In the case of Workflow A, what happened to the parent workflow?

In the case of Workflow B it behaves as designed as I believe the parent B doesn’t complete when timer fires.

Could you post histories for all 4 workflows?

Parent workflow A continues to the next activities, but now it is Completed. Child workflow A is terminated.

I think I have found out why workflow A and B behave differently. Child workflow A is terminated because parent workflow A is already completed. Child workflow B is not terminated, because parent workflow B is still running.

Now the question is, how to cancel a running child workflow.

  1. Using cancelHandler()

  2. Using ctx.Done()

But both do not work because the child workflow is still running.

(1) is the correct way.

childCtx.Done returns the channel that is closed upon cancellation. But just returning that channel is NOOP.

Check both parent and child histories for the cancellation request.

Hi Maxim,

(1) does send a cancellation request to child wf:


The child wf receives the request and the activity is cancelled.



It has the same behavior as workflow.RequestCancelExternalWorkflow(ctx, cwfId, “”)

Yes (2) does not do anything to the child workflow. Does it only work for ExecuteActivity() and timer?

In conclusion, cancelling a child workflow works using (1). I just realize that cancelation and termination are 2 different activities. I read a topic that says to create a different activity to cancel the child workflow using Temporal client.

1 Like

Look at the thread dump (stack trace tab in the UI) for the child workflow to see where it is blocked. In some cases, you have to handle context cancellation explicitly. For example, a selector has to handle the closure of context.Done channel.

It is currently blocked due to waiting for a signal.

The code below means the parent waits until the child context cancelation is done, right?

After the cancelation, can the child workflow continue to the next activities?

So the child doesn’t handle context cancellation as Channel.Receive is not interrupted by it.

The code snippet can be used to handle the context cancellation.

Yes, the child has not handled context cancelation.

I see, okay thank you for the help.