Signals and queries: the workflow as an actor

A running workflow is a long-lived actor you can message: a signal is a fire-and-forget input recorded as an event that can change the workflow's path, and a query is a read-only peek at current state that must never mutate or schedule anything.

Previously

A signal can divert ORDER #1001 away from shipping — but 'divert' is easy to say and hard to do correctly. We've already charged the card and reserved the Widget. We can't ship, and we can't just stop and leave the customer paying for nothing. The completed steps have to be UNDONE, in the right order. How does a workflow safely back out of work it already committed?

Scene 08

Signals and queries: the workflow as an actor

  1. Watch
  2. Try it
  3. Predict
  4. Capture
ORDER #1001 — a running workflow is an addressable actorcancelOrder signal: not sentcustomer“cancel?”fire-and-forgetno signalgetOrderStatusread-onlyno event appendedreserved, about t…MAIL—ORDER #1001workflow (replayed)paused before ShipPackageNEXT STEPShipPackageDEFAULT PATHShipPackage → EmailDIVERTED (SIGNAL)ReleaseInventory → Refund…EVENT HISTORY (append-only) — signals LAND here · queries append NOTHINGChargeCard ✓ $42replayedReserveInventory ✓replayedShipPackagependingA running workflow is an addressable actor: you can read it without leaving a trace, or send it input that changes …
paused before its next step — but still reachable
What to watch for

ORDER #1001 isn't finished — it's a LIVE workflow, paused on its next step (ShipPackage), waiting. That's the first surprise: a running workflow isn't a closed box you fire and forget. It's an addressable thing, sitting there with a mailbox, that you can still talk to. Watch the getOrderStatus arrow on the left peek IN: it reads the current state — "reserved, about to ship" — and a value comes back OUT. Now look at the event-history strip along the bottom: nothing new landed. The read left no trace. That trace-free, read-only peek into a running workflow's current state is called a query: it asks "what is the order's status right now?" and gets an answer without changing or recording anything. It runs against replayed state, so it must stay read-only.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

OrderWorkflow.run
the main path pauses on its mailbox before each step
def run(order):
charge_card(order) # recorded as events
reserve_inventory(order)
# the actor parks here, reachable, until woken
if self.cancelled:
return run_compensation(order) # divert
ship_package(order)
send_email(order)
@signal_handler cancelOrder
fire-and-forget input recorded as a durable event
# delivered to the running workflow's mailbox
def cancelOrder():
# lands as SignalReceived in the event history
self.cancelled = True
# next step sees it and diverts the path
@query_handler getOrderStatus
synchronous read-only peek — appends no event
# runs against REPLAYED state
def getOrderStatus():
# reads current state, returns it, no event appended
return self.status # 'reserved, about to ship'
# FORBIDDEN here: mutate state or schedule work,
# replay would diverge -> non-determinism error

Where this sits in Build a workflow engine (Temporal / Airflow / Cadence style)

Scene 08 of 13, in the Time & actors act — Durable timers and the workflow as an actor.. A running workflow is an addressable actor: a signal delivers external input durably and can change its path; a query reads its state without mutating it.

Up next. Diverting mid-order means undoing committed work — but you can't wrap a charge, a reservation, and a shipment in one database transaction across separate services. Instead each step gets a semantic undo (a refund undoes a charge) and on failure you run those undos in reverse order. That pattern is the saga.

All 13 scenes in Build a workflow engine (Temporal / Airflow / Cadence style) · Every curriculum

Built with Arqly
Every scene in Build a workflow engine (Temporal / Airflow / Cadence style) builds on the one before it.All 13 Build a workflow engine (Temporal / Airflow / Cadence style) scenes