What’s the best way to start a child workflow async?
The important thing is to start the workflow, then the child workflow does not need to be associated with the parent workflow any more.
What’s the best way to start a child workflow async?
The important thing is to start the workflow, then the child workflow does not need to be associated with the parent workflow any more.
Currently, it is not straightforward. We have plans to improve the experience (relevant issue), but in the meantime, the following should be done.
ChildWorkflowOptions.ParentClosePolicy
to ABANDON
when creating a child workflow stub.Promise
returned by getWorkflowExecution
to complete. This indicates that the child successfully started (or start failed).Steps 3 and 4 are needed to ensure that child workflow starts before the parent closes. If the parent initiates child workflow and immediately completes the child would never start. Here is the code that demonstrates the steps:
public void parentWorklfow() {
ChildWorkflowOptions options =
ChildWorkflowOptions.newBuilder()
.setParentClosePolicy(ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON)
.build();
MyChildWorkflow child = Workflow.newChildWorkflowStub(MyChildWorkflow.class, options);
Async.procedure(child::<workflowMethod>, <args>...);
Promise<WorkflowExecution> childExecution = Workflow.getWorkflowExecution(child);
// Wait for child to start
childExecution.get()
}
ChildWorkflowOptions.ParentClosePolicy
to ABANDON
when creating a child workflow stub.Steps 3 and 4 are needed to ensure that child workflow starts before the parent closes. If the parent initiates child workflow and immediately completes the child would never start. Here is the code that demonstrates the steps (error handling omitted for brevity):
func ParentWorkflow(ctx workflow.Context) error {
childWorkflow := workflow.ExecuteChildWorkflow(ctx, MyChildWorkflow)
// Wait for child to start
_ = childWorkflow.GetChildWorkflowExecution().Get(ctx, nil)
return nil
}
I am using an activity to do something like that. the activity just submits a workflow and returns! of course in my usecase, i really dont care about the outcome of the child workflow, hence it kind of works for me… but don know the consequences . @maxim any suggestions?
@madhu It also works, you should be careful about assigning ID to the child when starting it this way to avoid duplicated workflows if start times out for some reason.
Hi Maxim, is there a reason the cancellation type doesn’t need to be set to ABANDON when executing child workflows using the Go SDK?
Good catch. I actually specified incorrect option in both Java and Go cases. It should read:
ChildWorkflowOptions.ParentClosePolicy
to ABANDON
when creating a child workflow stub.I updated the original post.
Thanks for the quick response. I’m having some trouble determining the type of ChildWorkflowOptions.ParentClosePolicy
. The source code points to an enum in the generated protobuf api. but I can’t seem to find the values anywhere.
Here is protobuf definition.
Can parent workflows start child workflows with the exact same ID ?
ie can a parent with ID ABC start a child workflow with id ABC.
I have a use case the last action of a workflow should be to create a child workflow, fire and forget style, like explained above. The child needs to get the same ID as the parent.
It is not ever allowed to have two workflows with the same ID open at the same time. So the parent cannot start a child with its own id.
But the parent can call continue as new with any other type. Continue as new closes a workflow run and creates a new one atomically. So it achieves your goal with even simpler code.
ah great ! Excellent this was exactly what I was looking for !
What are the cases where childWorkflow.GetChildWorkflowExecution().Get(ctx, nil) could fail and then what can be done in error handling to try to start the Child again?
It can fail only if there is a workflow with the same WorkflowId
is already running or there is a closed workflow with the same WorkflowId
and WorkflowIdReusePolicy doesn’t allow starting another workflow.
Note that there was a bug around error handling in this case which was fixed by PR #418.
Hi @maxim!
I have a question regarding tests for async child workflows.
I have successfully implemented and tested live that using the ABANDON
policy when triggering a child workflow works correctly (parent terminates and the child continues its execution) but when writing unit tests the mock always fails as if activities in the child workflow are never executed.
Is there something I’m missing in the test environment for the test to succeed?
Here’s a working sample (taken from the go-samples
):
// workflow.go
package test_child
import (
"go.temporal.io/api/enums/v1"
"go.temporal.io/sdk/workflow"
)
func SampleParentWorkflow(ctx workflow.Context) error {
logger := workflow.GetLogger(ctx)
cwo := workflow.ChildWorkflowOptions{
WorkflowID: "ABC-SIMPLE-CHILD-WORKFLOW-ID",
ParentClosePolicy: enums.PARENT_CLOSE_POLICY_ABANDON,
}
ctx = workflow.WithChildOptions(ctx, cwo)
var result string
err := workflow.ExecuteChildWorkflow(ctx, SampleChildWorkflow, "World").GetChildWorkflowExecution().Get(ctx, &result)
if err != nil {
logger.Error("Parent execution received child execution failure.", "Error", err)
}
logger.Info("Parent execution completed.", "Result", result)
return nil
}
func SampleActivity(ctx workflow.Context, message string) error {
logger := workflow.GetLogger(ctx)
greeting := "Hello " + message + " from activity!"
logger.Info("Child activity execution: " + greeting)
return nil
}
func SampleChildWorkflow(ctx workflow.Context, name string) (string, error) {
logger := workflow.GetLogger(ctx)
greeting := "Hello " + name + "!"
logger.Info("Child workflow execution: " + greeting)
if err := workflow.ExecuteActivity(ctx, SampleActivity, name).Get(ctx, nil); err != nil {
return "", err
}
return greeting, nil
}
// workflow_test.go
package test_child
import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"go.temporal.io/sdk/testsuite"
"testing"
)
type UnitTestSuite struct {
suite.Suite
testsuite.WorkflowTestSuite
env *testsuite.TestWorkflowEnvironment
}
func TestUnitTestSuite(t *testing.T) {
suite.Run(t, new(UnitTestSuite))
}
func (s *UnitTestSuite) SetupTest() {
s.env = s.NewTestWorkflowEnvironment()
s.env.RegisterActivity(SampleActivity)
s.env.RegisterWorkflow(SampleChildWorkflow)
}
func (s *UnitTestSuite) AfterTest(suiteName, testName string) {
s.env.AssertExpectations(s.T())
}
func (s *UnitTestSuite) TestWorkflow() {
s.env.OnActivity(SampleActivity, mock.Anything).Return(nil)
s.env.ExecuteWorkflow(SampleParentWorkflow)
s.True(s.env.IsWorkflowCompleted())
}
Relevant logs:
=== RUN TestUnitTestSuite
--- FAIL: TestUnitTestSuite (0.00s)
=== RUN TestUnitTestSuite/TestWorkflow
2021/06/17 11:42:30 INFO ExecuteChildWorkflow WorkflowType SampleChildWorkflow
2021/06/17 11:42:30 INFO Child workflow execution: Hello World!
workflow_testsuite.go:771: FAIL: SampleActivity(string)
at: [workflow_testsuite.go:298 workflow_test.go:33]
workflow_testsuite.go:771: FAIL: 0 out of 1 expectation(s) were met.
The code you are testing needs to make 1 more call(s).
at: [workflow_testsuite.go:771 workflow_test.go:29 suite.go:137 suite.go:159]
--- FAIL: TestUnitTestSuite/TestWorkflow (0.00s)
I will look into this and report back later today.
The current implementation of test in sdk-go does not use ParentClosePolicy for child workflow. I have filed an issue here Test to support ParentClosePolicy · Issue #470 · temporalio/sdk-go · GitHub
Great thanks!
Are you accepting PRs for these things? Would love to contribute back to the project
A fix is ready: Add ParentClosePolicy to unit test by yiminc · Pull Request #471 · temporalio/sdk-go · GitHub
Hi Maxim, can you please confirm that when a parent workflow invokes continueAsNew, it closes the current instance of the workflow and starts a new one, resulting in the closure of any child workflows started by the parent, making it impossible for them to continue in the new instance of the parent workflow? Also, does setting ParentClosePolicy to ABANDON apply to child workflows when the parent continues as new? Thank you.
Continue as new results in closure of children unless they set ParentClosePolicy to ABANDON
. In the case of ABANDON
they keep running.