Pizzeria Temporale

Temporal workflow PoC — retries, signals, sagas in action

New Order

Active Orders

No orders yet. Place one above.

Temporal Concepts

Each pizza workflow step maps to a core Temporal feature. This reference explains what each concept is and where it appears.

Start Prepare Dough Rise (timer) Add Sauce* Add Toppings Bake* (timer) Quality Check (child) Package Deliver Done
* = 33% chance the baker drops the dough — Temporal retries automatically

Workflow pizzaOrderWorkflow

The top-level orchestrator. A durable function — if the worker crashes, Temporal replays event history and resumes exactly where it left off.

  • Must be deterministic: no Math.random(), no I/O
  • Runs in Temporal's sandboxed V8 isolate
  • All side effects live in activities
See it: Open Temporal UI → click any workflow for event history

Activity prepareDough, addSauce, etc.

Non-deterministic side-effect functions that do actual work (HTTP calls, DB writes). Run in the worker's Node.js process, outside the sandbox.

  • Failed activities retry without re-running the workflow
  • Each has its own timeout and retry policy
  • Registered on worker, invoked via proxyActivities
See it: Watch the log below each order — each line is an activity reporting progress

Retry Policy Baker drops the dough

When addSauce or bakeInOven throw (33% chance), Temporal waits then retries with exponential backoff.

  • Max attempts: 5
  • Backoff: 1s → 2s → 4s → 8s (cap 10s)
  • Workflow is paused during retries
See it: Watch for [retry 2/5] in the activity log

Signal Rush Order / Add Topping

External async messages sent into a running workflow to modify state mid-execution.

  • rushOrder: isRushed = true → timers shortened
  • addExtraTopping: appends to toppings array
  • Signals are durable — persisted even if worker is down
Try it: Click Rush during "Rising" or add a topping before "Toppings" step

Query getOrderStatus

Synchronous read-only requests to a running workflow. Return immediately, don't modify state or appear in event history.

  • Returns current step, progress, toppings, rush status
  • GET /orders/:idhandle.query()
Try it: curl localhost:3000/orders/<id> while running

Timer Dough rising / Bake

sleep() is a durable timer persisted in event history — not setTimeout.

  • Worker crash during sleep? Timer state is preserved on replay
  • Rising: 5s (1s rushed) / Baking: 8s (4s rushed)
Try it: Rush an order to see timers shortened

Child Workflow qualityCheckWorkflow

Starts another workflow as a child with its own ID, event history, and independent query/signal capabilities.

  • Separate retry/timeout policies
  • Visible as separate entry in Temporal UI
  • Reusable across parent workflows
See it: Look for quality-check-* child workflows in Temporal UI

Cancellation + Saga Compensation

Workflow runs inside CancellationScope.cancellable(). Cancel triggers saga compensation:

  • Pending activities/timers are cancelled
  • If baked: run refundOrder
  • Always: run releaseIngredients
Try it: Cancel during baking → see refund + release in log

Heartbeat Baking progress

Long-running activities heartbeat to tell Temporal "I'm alive". Missing heartbeats trigger rescheduling to another worker.

  • Payload (e.g. "baking 75%") enables resume
See it: Watch "Baking... 25%/50%/75%/100%" progress

Task Queue + Worker

Workers poll a named task queue for work. The worker is a separate process from NestJS — decoupled execution.

  • Scale workers independently from the API
  • Workflows survive worker restarts (replay)
  • Multiple workers can share a queue