Build a workflow engine (Temporal / Airflow / Cadence style) (13 scenes)
Scene 10 · Child workflows and ContinueAsNew
Child workflows isolate sub-units with their own histories, and ContinueAsNew restarts an endless workflow with a fresh history but the same ID before it hits the limit.
Previously
Child workflows and ContinueAsNew let ORDER #1001 compose and run forever with small histories. But running forever surfaces the deepest problem of all: a workflow started under today's code might still be sleeping a year from now, and by then you'll have deployed v2. When that old execution wakes and replays against new code, the recorded history won't match — and we know what that means. How do you change workflow code safely?
Scene 10
Child workflows and ContinueAsNew
Diagram
LEFT: ORDER #1001 split across warehouses — the parent starts 'child workflows' (each a separate execution with its OWN history lane), isolating failures and keeping each history small. RIGHT: an endless loop's history bar filling toward a red 'max events (~50k)' line — past it, the engine terminates the execution. 'ContinueAsNew' is the fix: at a safe checkpoint it atomically restarts the workflow with the SAME ID but a fresh empty history, carrying only a small summary forward, so the logical workflow runs forever while each execution stays small.
child workflow: a separate execution per shipment, with its own small history
one history per child → a failure in one is isolated from the other
ORDER #1001 ships from two warehouses now, so the order has two shipments. Instead of cramming both into one giant order workflow with one ever-growing history, the parent ORDER #1001 starts a SEPARATE execution for each shipment — each with its OWN little event history (the green lane inside each box). If WH-East's shipment fails, WH-West's is untouched, and neither history balloons. That separate, parent-started execution is a **child workflow**. Now look at the right panel: a different workflow is a subscription that loops 'wait, then bill' indefinitely in a SINGLE execution. Every iteration appends events to that one history, so its bar keeps climbing toward the red line. Watch where that line is.
Implementation
OrderWorkflow.run (parent)
one child execution per shipment — separate histories
1def run(order):2 children = []3 for shipment in order.shipments:4 # each child is a SEPARATE execution5 # with its OWN event history6 h = start_child_workflow(7 ShipmentWorkflow, shipment,8 )9 children.append(h)10 # a failure in one child is isolated11 await gather(children)
SubscriptionWorkflow.run (one execution)
every iteration appends events to one growing history
1def run(state):2 while True:3 await sleep(30 * DAYS) # TimerStarted/Fired4 bill(state.customer) # Activity events5 state.cycle += 16 # history keeps growing toward the hard limit:7 # warn ~10k events, terminate past ~50k / ~50MB8 if history.size() > MAX_EVENTS:9 terminate(self) # engine kills the run
SubscriptionWorkflow.run (with ContinueAsNew)
at a checkpoint, restart with same ID and empty history
1def run(state):2 while True:3 await sleep(30 * DAYS)4 bill(state.customer)5 state.cycle += 16 if history.size() > CHECKPOINT:7 # atomic: close this execution, open a fresh8 # one — SAME workflow ID, EMPTY history,9 # only a small summary carried forward10 continue_as_new(summary=state)
Not sure what to ask? Tap a question — the staff engineer answers in the chat panel.