A Dependency Graph
A nightly reconciliation job reads two upstream sources, compares them, and posts one adjustment. The reads share an account lookup and can run in parallel. The write can fail after the adjustment is reserved, so it needs a compensating action. The job also holds two handles — a database connection and an advisory lock — that must be released even when a step fails.
Each failure belongs to a different stage, so each stage gets the module that owns it.
flowchart TD Start["Graph.run(input)"] --> Ledger["ledger node"] Start --> Bank["bank node"] Ledger --> Account["account node (shared)"] Bank --> Account Ledger --> Diff["difference node"] Bank --> Diff Diff --> Tx["Transaction: reserve, then post"] Tx -->|post fails| Undo["undo reserve"] Tx --> Done["Ok(acc)"]
Schedule the reads
Section titled “Schedule the reads”Each node returns a Task.Result. A node starts when its dependencies succeed, and the shared
account lookup runs once per graph run even though two nodes depend on it.
A failure in bank prevents difference from running and signals ledger to stop. The
Graph guide covers concurrency limits, shared-node execution, and the Aborted
outcome.
Write it under compensation
Section titled “Write it under compensation”The write is two external calls. If the second fails, the first must be reversed. A Transaction
runs steps in order and unwinds completed steps with their undo action in reverse order. The steps
close over the handles, so the same transaction can run against any connection:
post reads reserve’s output through deps, so step order and dependency names are checked when
the transaction is assembled. The Transaction guide explains the step
context, the accumulated record, and how undo failures are reported instead of swallowed.
Hold the handles
Section titled “Hold the handles”The job needs a connection and a lock for the whole run. Resource acquires them in order and
releases them in reverse order once the work settles, including when it settles with an error.
runGraph returns a function, so the graph starts when the resource work starts, not when the
module loads. The run parameter is annotated because the resource arrives after the options; the
Resource guide covers chain, combine, and the release contract.
Failure behavior
Section titled “Failure behavior”Read failures settle the graph and cancel its siblings. Write failures settle the transaction with
Transaction.Failure, which names the failed step and lists any undo errors. Release tasks are
infallible: a close failure is handled inside the release task, not in the returned error channel.
Cancellation reaches every stage as Aborted. If the operator aborts between
the graph and the transaction, the transaction settles before starting a step and no write runs.
The transaction does not abort the graph. Sequence them inside Resource.use when the handles must
outlive both, as above, or compose them inside a single Op when the whole job needs
retry and timeout policy.