new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Temporal · all subjects

child workflows

78 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

Child Workflow related events logged to Event History

When using a Child Workflow API, Child Workflow related Events (StartChildWorkflowExecutionInitiated, ChildWorkflowExecutionStarted, ChildWorkflowExecutionCompleted, etc.) are logged in the Workflow Execution Event History.

.NET awaiting StartChildWorkflowAsync or ExecuteChildWorkflowAsync waits for ChildWorkflowExecutionStarted Event

In .NET, awaiting StartChildWorkflowAsync() or ExecuteChildWorkflowAsync() internally waits for the ChildWorkflowExecutionStarted Event before returning, so the Child Workflow is guaranteed to have started once the call resolves.

Child Workflow started from Signal or Update handler must resolve before Parent completion

If you start a Child Workflow from a non-main context (for example, a Signal or Update handler), make sure the Parent Workflow doesn't complete before that call resolves.

ExecuteChildWorkflowAsync starts Child Workflow and waits for completion

The ExecuteChildWorkflowAsync() method starts the Child Workflow and waits for completion.

StartChildWorkflowAsync starts Child Workflow and returns handle

The StartChildWorkflowAsync() method starts a Child Workflow and returns its handle. This is useful if you want to do something after it has only started, or to get the Workflow/Run ID, or to be able to signal it while running.

ExecuteChildWorkflowAsync is helper for StartChildWorkflowAsync plus await handle.GetResultAsync

ExecuteChildWorkflowAsync() is a helper method for StartChildWorkflowAsync() plus await handle.GetResultAsync().

Default Parent Close Policy is terminate

The default Parent Close Policy option is set to terminate the Child Workflow Execution.

Set ParentClosePolicy in ChildWorkflowOptions for .NET

Set the ParentClosePolicy property inside the ChildWorkflowOptions for ExecuteChildWorkflowAsync or StartChildWorkflowAsync to specify the behavior of the Child Workflow when the Parent Workflow closes.

.NET example: Execute Child Workflow with default settings

await Workflow.ExecuteChildWorkflowAsync((MyChildWorkflow wf) => wf.RunAsync());

.NET example: Execute Child Workflow with Abandon Parent Close Policy

await Workflow.ExecuteChildWorkflowAsync( (MyChildWorkflow wf) => wf.RunAsync(), new() { ParentClosePolicy = ParentClosePolicy.Abandon });

Parent Close Policy enum values in Go

The possible Parent Close Policy values in Go are: - PARENT_CLOSE_POLICY_ABANDON - PARENT_CLOSE_POLICY_TERMINATE - PARENT_CLOSE_POLICY_REQUEST_CANCEL Type: ParentClosePolicy Default: PARENT_CLOSE_POLICY_TERMINATE

Parent Close Policy example in Go

Example of setting Parent Close Policy in Go: import ( // ... "go.temporal.io/api/enums/v1" ) func YourWorkflowDefinition(ctx workflow.Context, params ParentParams) (ParentResp, error) { // ... childWorkflowOptions := workflow.ChildWorkflowOptions{ // ... ParentClosePolicy: enums.PARENT_CLOSE_POLICY_ABANDON, } ctx = workflow.WithChildOptions(ctx, childWorkflowOptions) childWorkflowFuture := workflow.ExecuteChildWorkflow(ctx, YourOtherWorkflowDefinition, ChildParams{}) // ... } func YourOtherWorkflowDefinition(ctx workflow.Context, params ChildParams) (ChildResp, error) { // ... return resp, nil }

Child Workflow Execution definition in Go

A Child Workflow Execution is a Workflow Execution that is scheduled from within another Workflow using a Child Workflow API. When using a Child Workflow API, Child Workflow related Events (StartChildWorkflowExecutionInitiated, ChildWorkflowExecutionStarted, ChildWorkflowExecutionCompleted, etc.) are logged in the Workflow Execution Event History.

ChildWorkflowExecutionStarted Event requirement before parent completion

The ChildWorkflowExecutionStarted Event must be logged to the Event History before the Parent Workflow completes to ensure the Child Workflow has started. In Go, you must explicitly call GetChildWorkflowExecution() on the ChildWorkflowFuture and then call Get() on the returned Future to wait for this Event.

ExecuteChildWorkflow API in Go

To spawn a Child Workflow Execution in Go, use the ExecuteChildWorkflow API from the go.temporal.io/sdk/workflow package. The ExecuteChildWorkflow call requires an instance of workflow.Context with an instance of workflow.ChildWorkflowOptions applied to it, the Workflow Type, and any parameters to pass to the Child Workflow Execution. The ExecuteChildWorkflow call returns an instance of ChildWorkflowFuture.

ChildWorkflowOptions field inheritance in Go

workflow.ChildWorkflowOptions contain the same fields as client.StartWorkflowOptions. Workflow Option fields automatically inherit their values from the Parent Workflow Options if they are not explicitly set. If a custom WorkflowID is not set, one is generated when the Child Workflow Execution is spawned.

WithChildOptions API to apply Child Workflow Options

Use the WithChildOptions API to apply Child Workflow Options to an instance of workflow.Context. This is then passed to the ExecuteChildWorkflow call.

Waiting for Child Workflow results with ChildWorkflowFuture

Call the .Get() method on an instance of ChildWorkflowFuture to wait for the result of the Child Workflow Execution.

Async Child Workflows in Go - basic synchronous example

Example of synchronous child workflow execution in Go: func YourWorkflowDefinition(ctx workflow.Context, params ParentParams) (ParentResp, error) { childWorkflowOptions := workflow.ChildWorkflowOptions{} ctx = workflow.WithChildOptions(ctx, childWorkflowOptions) var result ChildResp err := workflow.ExecuteChildWorkflow(ctx, YourOtherWorkflowDefinition, ChildParams{}).Get(ctx, &result) if err != nil { // ... } // ... return resp, nil } func YourOtherWorkflowDefinition(ctx workflow.Context, params ChildParams) (ChildResp, error) { // ... return resp, nil }

Async Child Workflows requirement - Abandon Parent Close Policy

To asynchronously spawn a Child Workflow Execution, the Child Workflow must have an 'Abandon' Parent Close Policy set in the Child Workflow Options. Additionally, the Parent Workflow Execution must wait for the ChildWorkflowExecutionStarted Event to appear in its Event History before it completes.

Async Child Workflows - Parent completion risk

If the Parent makes the ExecuteChildWorkflow call and then immediately completes, the Child Workflow Execution does not spawn.

Async Child Workflows - waiting for execution to start

To ensure that the Child Workflow Execution has started, first call the GetChildWorkflowExecution method on the instance of the ChildWorkflowFuture, which will return a different Future. Then call the Get() method on that Future, which will wait until the Child Workflow Execution has spawned.

Async Child Workflows in Go - with Abandon Parent Close Policy example

Example of asynchronous child workflow execution in Go with Abandon Parent Close Policy: import ( // ... "go.temporal.io/api/enums/v1" ) func YourWorkflowDefinition(ctx workflow.Context, params ParentParams) (ParentResp, error) { childWorkflowOptions := workflow.ChildWorkflowOptions{ ParentClosePolicy: enums.PARENT_CLOSE_POLICY_ABANDON, } ctx = workflow.WithChildOptions(ctx, childWorkflowOptions) childWorkflowFuture := workflow.ExecuteChildWorkflow(ctx, YourOtherWorkflowDefinition, ChildParams{}) // Wait for the Child Workflow Execution to spawn var childWE workflow.Execution if err := childWorkflowFuture.GetChildWorkflowExecution().Get(ctx, &childWE); err != nil { return err } // ... return resp, nil } func YourOtherWorkflowDefinition(ctx workflow.Context, params ChildParams) (ChildResp, error) { // ... return resp, nil }

Parent Close Policy definition in Go

A Parent Close Policy determines what happens to a Child Workflow Execution if its Parent changes to a Closed status (Completed, Failed, or Timed Out).

Parent Close Policy default value in Go

The default Parent Close Policy option is set to terminate the Child Workflow Execution.

Parent Close Policy configuration in Go

In Go, a Parent Close Policy is set on the ParentClosePolicy field of an instance of workflow.ChildWorkflowOptions. The possible values can be obtained from the go.temporal.io/api/enums/v1 package.

Child Workflow independent execution and history

Each child Workflow executes as an independent Workflow with its own Workflow ID, event history (50K event limit), and lifecycle. Child Workflows maintain separate histories, preventing parent history bloat.

Child Workflow key capabilities

Child Workflows provide: independent identity with unique Workflow ID visible in UI for tracking and querying, separate event history, flexible invocation via synchronous (blocking) or asynchronous (non-blocking) execution, lifecycle control through Parent Close Policy, Task Queue routing to specialized Workers, and reusability across multiple parent Workflows.

Child Workflow vs Activity distinction

Activities execute code (especially external operations like API calls or database queries), while Child Workflows orchestrate processes. Use Child Workflows when you need a separate Workflow ID, independent tracking, operations that may outlive the parent, reuse across multiple parents, execution on different Task Queues, independent history and event limits, or different timeouts/retry policies at Workflow level. Use Activities for short-lived external operations tightly coupled to parent lifecycle when lower overhead is important.

ParentClosePolicy values and behavior

ParentClosePolicy determines Child Workflow behavior when parent closes. TERMINATE: child is terminated when parent closes (for tightly coupled processes). ABANDON: child continues independently (for fire-and-forget, long-running tasks). REQUEST_CANCEL: child receives cancellation request (for graceful cleanup).

Synchronous vs asynchronous child execution in Python

In Python, workflow.execute_child_workflow() starts a child Workflow and awaits its completion (synchronous/blocking). workflow.start_child_workflow() starts a child Workflow asynchronously, returning a handle once the child has started without waiting for completion.

Synchronous vs asynchronous child execution in Go

In Go, workflow.ExecuteChildWorkflow() returns a ChildWorkflowFuture. Calling .Get() on the future blocks until the child completes (synchronous). To execute asynchronously, call ExecuteChildWorkflow in a loop without calling .Get() immediately; later call .Get() on each future to collect results. childFuture.GetChildWorkflowExecution().Get() blocks until the child has started (not when it completes).

Synchronous vs asynchronous child execution in Java

In Java, Workflow.newChildWorkflowStub() creates a typed stub and calling a method on it blocks the parent (synchronous). Async.function() starts a child Workflow asynchronously. Workflow.getWorkflowExecution(child) returns a Promise that resolves when the child starts (not when it completes).

Synchronous vs asynchronous child execution in TypeScript

In TypeScript, executeChild() starts a child Workflow and awaits its completion (synchronous/blocking). startChild() returns a handle once the child has started without waiting for completion (asynchronous).

Fire-and-forget pattern requirement

When using fire-and-forget pattern with asynchronous child execution, you must wait for the child to start before the parent completes. Without this, the parent could complete before the child is scheduled and the child would never execute. The ABANDON policy ensures the child continues running after the parent completes.

Parallel child execution pattern

To execute multiple Child Workflows in parallel, start all children first (which returns immediately), then wait for all of them to complete. In Python use asyncio.gather(), in TypeScript use Promise.all(), in Java use Promise.allOf().get(), in Go use a loop to call ExecuteChildWorkflow for each child, then loop again calling .Get() on each future.

Child Workflow failure handling

Child Workflow failures propagate to the parent as a Child Workflow Failure exception (ChildWorkflowFailure in TypeScript and Java, ChildWorkflowError in Python, ChildWorkflowExecutionError in Go), with the underlying cause in its cause field. If not caught and handled, the parent Workflow fails as well.

Child Workflow benefits

Child Workflows provide modularity by breaking complex logic into reusable units. Each child is a first-class Workflow with its own ID for tracking, its own 50K event history limit, and its own execution timeout configuration. Children can outlive parents with ABANDON policy. Multiple children can execute concurrently. Child failures do not automatically fail the parent. The same Child Workflow can be reused by multiple parents.

Child Workflow trade-offs

Each child is a separate Workflow execution with its own history (overhead). There are more moving parts than a single monolithic Workflow. Child execution details are not in the parent history but are queryable independently. Async children require explicit synchronization if needed. More Workflow executions mean higher resource usage. Starting a Child Workflow has more overhead than starting an Activity.

Common pitfall: treating Child Workflows like Activities

Child Workflows are for orchestration, not for executing external code. If you only need to call an API or run a function, use an Activity instead. Do not use Child Workflows as a substitute for Activities.

Common pitfall: spawning unbounded children

Starting thousands of Child Workflows without batching can overwhelm the Temporal Service and bloat the parent's event history. Use fixed-size batches or a sliding window pattern instead.

Common pitfall: ignoring Parent Close Policy default

The default ParentClosePolicy is TERMINATE, which kills children when the parent closes. If children must outlive the parent, you must set the policy to ABANDON explicitly.

Common pitfall: synchronous calls blocking parent

Calling a Child Workflow synchronously blocks the parent until the child completes. For long-running children, use the async API to avoid stalling the parent.

Common pitfall: omitting Workflow IDs

Without explicit Workflow IDs, you lose the ability to deduplicate or look up Child Workflows by a meaningful identifier. Generate deterministic IDs based on business keys.

Child Workflow best practice: unique Workflow IDs

Generate unique IDs for Child Workflows to avoid conflicts. Use deterministic IDs based on business keys for deduplication and lookup.

Child Workflow best practice: choose appropriate policy

Use TERMINATE for tightly coupled children and ABANDON for independent children. Set timeouts appropriately for child executions.

Child Workflow best practice: limit parallelism

Do not spawn unlimited children; use batch patterns for large datasets. Use fixed-size batches or sliding window patterns.

Child Workflow best practice: use typed stubs

Prefer typed stubs over untyped for compile-time safety. Monitor child executions by tracking Child Workflow IDs for observability and debugging.

Conditional child execution pattern

You can conditionally start different Child Workflows based on business logic. The parent checks conditions and only starts specific Child Workflows when needed, allowing selective execution paths.

Child Workflow vs alternatives comparison

Child Workflow: High modularity, independent history, can outlive parent (ABANDON), medium overhead, separate Workflow ID. Activity: Medium modularity, no independent history, cannot outlive parent, low overhead, no separate ID. Separate Workflow + Signals: High modularity, independent history, can outlive parent, high overhead, separate ID. Async Lambda: Low modularity, no independent history, cannot outlive parent, very low overhead, no separate ID.

Fan-Out pattern: split records into fixed-size chunks with one child Workflow per chunk

The Fan-Out pattern distributes a large record set across multiple independent child Workflows, each responsible for processing a fixed-size chunk. The parent Workflow assigns work by passing only an offset and length (two integers per child) rather than explicit record IDs. This keeps history events small and allows each chunk to be processed independently with automatic retries of failed children without re-processing records handled by other children.

Fan-Out pattern problem: single Workflow limits

A single Workflow run can have at most 2,000 in-flight Activities (aim for 500) and at most 50,000 history events. Processing millions of records in a single Workflow run is not possible, necessitating partitioning strategies like Fan-Out.

Fan-Out pattern: when to use and concurrency limits

Use Fan-Out when you want maximum concurrency with no rate control and you can pre-compute how many chunks you need before the job starts. Keep the number of in-flight children per parent well under the default limit of 2,000. For larger workloads, use Sliding Window or Batch Iterator patterns instead.

Fan-Out pattern implementation: parent and child Workflow structure

The parent Workflow receives the total record count and chunk size, divides the total into chunks, and starts one child Workflow per chunk passing only offset and length. Each child independently fetches its slice of records and executes an activity for each record (like processRecord). The parent blocks until all children complete, then returns aggregated results.

Fan-Out best practice: use offset and length instead of explicit record IDs

Pass only two integers (offset and length) to each child Workflow rather than a full slice of record IDs. The child fetches its own records. This approach keeps history events small and avoids storing millions of IDs in event history.

Fan-Out best practice: chunk size relative to Activity limit

Size chunks to stay under the Activity limit of 2,000 in-flight Activities per child Workflow. Aim for chunks of 500 records or fewer if each record maps to one Activity.

Fan-Out best practice: set deterministic Workflow IDs for children

Give each child Workflow a deterministic ID using the pattern parentId/batch-<offset>. This makes it safe to re-run the parent because Temporal deduplicates child starts by Workflow ID, ensuring already-completed children are not re-executed.

Fan-Out best practice: cap concurrent child starts

Starting thousands of child Workflows simultaneously puts pressure on the namespace. Consider batching child starts or using Sliding Window pattern if tighter concurrency control is needed.

Fan-Out best practice: PARENT_CLOSE_POLICY_ABANDON for fire-and-forget

Set PARENT_CLOSE_POLICY_ABANDON for fire-and-forget fan-outs where the parent does not need to collect results. With the default TERMINATE policy, cancelling or timing out the parent will terminate all in-flight children.

Fan-Out pitfall: starting too many children at once

Each child start adds to the parent's history. Temporal enforces a default limit of 2,000 pending (in-flight) child Workflows per parent. Keep well under this limit. If you need more children, switch to MapReduce Tree or Sliding Window patterns.

Give your agent this brain