Child workflows
Besides activities, a workflow can also orchestrate other workflows.
workflow.ExecuteChildWorkflow
enables the scheduling of other workflows from within a workflow's
implementation. The parent workflow has the ability to monitor and impact the lifecycle of the child
workflow, similar to the way it does for an activity that it invoked.
public static class GreetingWorkflowImpl implements GreetingWorkflow {
@Override
public String getGreeting(String name) {
// Workflows are stateful. So a new stub must be created for each new child.
GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
// This is a non blocking call that returns immediately.
// Use child.composeGreeting("Hello", name) to call synchronously.
Promise<String> greeting = Async.function(child::composeGreeting, "Hello", name);
// Do something else here.
return greeting.get(); // blocks waiting for the child to complete.
}
// This example shows how parent workflow return right after starting a child workflow,
// and let the child run itself.
private String demoAsyncChildRun(String name) {
GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
// non blocking call that initiated child workflow
Async.function(child::composeGreeting, "Hello", name);
// instead of using greeting.get() to block till child complete,
// sometimes we just want to return parent immediately and keep child running
Promise<WorkflowExecution> childPromise = Workflow.getWorkflowExecution(child);
childPromise.get(); // block until child started,
// otherwise child may not start because parent complete first.
return "let child run, parent just return";
}
}
Workflow.newChildWorkflowStub
returns a client-side stub that implements a child workflow interface.
It takes a child workflow type and optional child workflow options as arguments. Workflow options may be needed to override
the timeouts and task_list if they differ from the ones defined in the @WorkflowMethod
annotation or parent workflow.
The first call to the child workflow stub must always be to a method annotated with @WorkflowMethod
. Similar to activities, a call
can be made synchronous or asynchronous by using Async#function
or Async#procedure
. The synchronous call blocks until a child workflow completes. The asynchronous call
returns a Promise
that can be used to wait for the completion. After an async call returns the stub, it can be used to send signals to the child
by calling methods annotated with @SignalMethod
. Querying a child workflow by calling methods annotated with @QueryMethod
from within workflow code is not supported. However, queries can be done from activities
using the provided WorkflowClient
stub.
Running two children in parallel:
public static class GreetingWorkflowImpl implements GreetingWorkflow {
@Override
public String getGreeting(String name) {
// Workflows are stateful, so a new stub must be created for each new child.
GreetingChild child1 = Workflow.newChildWorkflowStub(GreetingChild.class);
Promise<String> greeting1 = Async.function(child1::composeGreeting, "Hello", name);
// Both children will run concurrently.
GreetingChild child2 = Workflow.newChildWorkflowStub(GreetingChild.class);
Promise<String> greeting2 = Async.function(child2::composeGreeting, "Bye", name);
// Do something else here.
...
return "First: " + greeting1.get() + ", second: " + greeting2.get();
}
}
To send a signal to a child, call a method annotated with @SignalMethod
:
public interface GreetingChild {
@WorkflowMethod
String composeGreeting(String greeting, String name);
@SignalMethod
void updateName(String name);
}
public static class GreetingWorkflowImpl implements GreetingWorkflow {
@Override
public String getGreeting(String name) {
GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
Promise<String> greeting = Async.function(child::composeGreeting, "Hello", name);
child.updateName("Cadence");
return greeting.get();
}
}
Calling methods annotated with @QueryMethod
is not allowed from within workflow code.