monitor_demo — data for the monitor

What it does

Seeds a SQLite store with executions in every state the monitor can show, then points the monitor at it. Every execution is produced by running real machines — none is written by hand, except one whose machine is deliberately missing:

  • orders in the cart, waiting for payment on a sub-state with a durable timer armed, suspended, delivered, and one dead-lettered by a card charge that raises;

  • an orthogonal fulfilment mid-join, and one ended by cancel_on_failure, its other region cancelled;

  • an execution of a definition the monitor isn’t given, to show it degrades to data-only.

Each carries its execution trace, so the monitor’s timeline has steps to show.

The machines

An order, with a composite Checkout and a timeout on its Payment step:

        stateDiagram-v2
[*] --> Cart
state "Checkout<br/>on enter: reserve_inventory" as Checkout {
  [*] --> Checkout_Payment
  state "Payment" as Checkout_Payment
  Checkout_Payment : timeout#58; 300
  state "Confirm" as Checkout_Confirm
  Checkout_Confirm : on enter#58; charge_card
  Checkout_Payment --> Checkout_Confirm : Paid<br/>/ context.charge_id = event.charge_id
  Checkout_Confirm --> [*]
}
state "Delivered" as Delivered
Delivered : on enter#58; notify_customer
Delivered : outcome#58; success
Cart --> Checkout : Checkout
Checkout --> Shipped : Ship<br/>/ context.tracking = event.tracking
Shipped --> Delivered : Deliver
Delivered --> [*]
Checkout_Payment --> Cart : Timeout
    

A fulfilment, picking and billing in parallel regions, with cancel_on_failure:

        stateDiagram-v2
[*] --> Fork
state Fork {
  state "Picking" as Fork_Picking {
    [*] --> Fork_Picking_P1
    state "P1" as Fork_Picking_P1
    state "P2" as Fork_Picking_P2
    Fork_Picking_P2 : outcome#58; success
    state "P3" as Fork_Picking_P3
    Fork_Picking_P3 : outcome#58; failed
    Fork_Picking_P1 --> Fork_Picking_P2 : Picked
    Fork_Picking_P1 --> Fork_Picking_P3 : PickFailed
    Fork_Picking_P2 --> [*]
    Fork_Picking_P3 --> [*]
  }
  --
  state "Billing" as Fork_Billing {
    [*] --> Fork_Billing_B1
    state "B1" as Fork_Billing_B1
    state "B2" as Fork_Billing_B2
    Fork_Billing_B2 : outcome#58; success
    Fork_Billing_B1 --> Fork_Billing_B2 : Billed
    Fork_Billing_B2 --> [*]
  }
}
state "Done" as Done
Done : outcome#58; success
state "Failed" as Failed
Failed : outcome#58; failed
state Fork__join_success <<choice>>
Fork --> Fork__join_success
Fork__join_success --> Done : join_success=pass
Fork__join_success --> Failed : else
Done --> [*]
Failed --> [*]
    

How it is built

seed.py builds one DurableRunner over a SqliteStore with trace=True and an injected clock that starts an hour ago and moves a few seconds per step, so the timestamps differ. It creates each execution with a chosen id and feeds it events (runner.suspend(...) for the suspended one); actions.py’s charge_card raises when the order’s context says decline, leaving a real dead letter.

        sequenceDiagram
  participant Seed as seed.py
  participant R as DurableRunner (trace on)
  participant S as SqliteStore
  participant M as harel monitor
  Seed->>R: create(Order, "order-delivered") + Checkout, Paid, Ship, Deliver
  R->>S: commit each step, with its trace step
  Seed->>R: create(Order, "order-failed", decline) + Checkout, Paid
  R->>S: commit: FAILED (the charge raised, and nothing models it)
  Note over Seed,S: and the other executions, the same way
  M->>S: list_executions, load, read_trace
    

How it runs

  • Semantics: synchronous — DurableRunner: each step is committed before the next.

  • Execution model: background, the default: a one-off script.

  • The trace is opt-in (trace=True): one timeline step per event, written in the same commit as the step.

Run it

uv run python -m examples.monitor_demo.seed /tmp/harel-demo.db
HAREL_STORE_BACKEND=sqlite HAREL_STORE_DB=/tmp/harel-demo.db \
    uv run harel monitor --definitions-dir examples/monitor_demo/machines