What is the best way to wait for child workflows in parent workflow in golang?

Hi,
I need to initiate a set of child workflows in parent workflow, and the parent workflow needs to wait for all executions of child workflows to be finished.

What is the best way to do it in golang? there must be better ways than using goroutine

Waiting for these does not use a traditional goroutine but uses the deterministic one built for workflows. One way would be to add all child workflow futures to a workflow.NewSelector and wait (see Temporal Go SDK developer's guide | Temporal Documentation) but that can be hard to know which completed first.

Probably the best way currently is to start and wait on the child workflow in a workflow.Go that then sends its result over a workflow.Channel that can be received from for the number children that were started. Is there a concern using workflow.Go this way?

Thanks for the reply. No, there is no concert for using workflow.Go. Ideally, I would like to have a way to start / cancel a group of workflows at the same time and even be able to add new workflow into the “execution group”. I know that parent workflow is not designed for this, just trying to work around it. But seems that adding new workflow into a group is not something the parent workflow can do, is there anything that i am missing which can do that?

But seems that adding new workflow into a group is not something the parent workflow can do, is there anything that i am missing which can do that?

No, I am afraid this would have to be a custom construct. You can maybe create something like (untested, just hand-typed here in forum):

type workflowGroup struct {
  ctx workflow.Context
  ctxCancel workflow.CancelFunc
  resultCh workflow.Channel
  childCount int
}

func newWorkflowGroup(ctx workflow.Context) *workflowGroup {
  w := &workflowGroup{resultCh: workflow.NewChannel(ctx)
  w.ctx, w.ctxCancel = workflow.WithCancel(ctx)
}

func (w *workflowGroup) startChild(childWorkflow interface{}, args ...interface{}) {
  w.childCount++
  fut := workflow.ExecuteChildWorkflow(w.ctx, childWorkflow, args...)
  workflow.Go(w.ctx, func(ctx workflow.Context) {
    w.resultCh.Send(ctx, fut.Get(ctx, nil))
  })
}

func (w *workflowGroup) waitForAll() (errs []error, ok bool) {
  ok = true
  for i := 0; i < w.childCount && ok; i++ {
    sel := workflow.NewSelector(w.ctx)
    sel.AddReceive(w.ctx.Done(), func(workflow.ReceiveChannel, bool) {
      ok = false
    })
    sel.AddReceive(w.resultCh, func(c workflow.ReceiveChannel, _ bool) {
      var err error
      c.Receive(w.ctx, &err)
      errs = append(errs, err)
    })
  }
  return
}

func (w *workflowGroup) cancel() {
  w.ctxCancel()
}

There is probably a lot wrong with the implementation, but the idea is there.

appreciated! seems that i am not able to add new child anymore once the parent has started waitForAll right?

In this primitive implementation, yes, but this is just an example. You may have to tailor for your needs.

IMHO the following is much simpler:

var children []workflow.Future
for ... {
   children = append(children, ExecuteChildWorkflow(...))
}

for _, child := range children {
    child.Get(...);
}
4 Likes