rp-dag
Purpose
Owns the workflow triggers and the execution log. It executes nothing.
Responsibility boundary
Two things belong here and nothing else:
- Triggers — "when this bus topic appears, run that workflow". System
configuration: tens of rows an operator maintains, held whole in the
runtime's memory rather than queried. - The execution log — a tree of what a run did. The runtime writes it while
the script runs: a node is opened before its body executes and closed when it
finishes, so a run in flight shows the node it is sitting on.
The workflow catalogue is not owned here. Ptah puts the active Solution's
descriptors into this service's environment (, WORKFLOWS,WORKFLOW_DIGESTS) and MODULE_PROXY republishes them for the runtimelistAvailableWorkflows
and the UI. Source bytes stay behind Ptah-proxy.
The log is written by the runtime, not by this service
Nothing here writes a log entry. The runtime formats it, puts it in Valkey under
a key it composes itself, and later hands over the keys — never the entries. turns each key into a store location and tells storage to pick thecommitLog
entry up; storage reads the cache directly, so an entry crosses the transport
once, as bytes nobody re-encodes.
workflow thread ─► queue ─► log writer ─► valkey
│
└─ commitLog([keys]) ─► rp-dag ─► storage reads valkey
│
◄──── committed ────────────────────────┘
└─ delete the committed keys
That is what keeps logging off the workflow's critical path: a node costs the
runtime a queue append and nothing else. It also means the log is best effort by
construction — an entry may be dropped under backpressure, and a batch may be
committed twice after a crash. Keys are derived from the run and the node's
sequence, so the second commit is a rewrite rather than a duplicate.
Keys come from the runtime for that reason: a number handed out by this service
would cost a round trip per node and would not be reproducible after a restart.
— the rundag:log:<executionId>:exec— one of its nodes, zero-padded to six digitsdag:log:<executionId>:n:<seq>
derives the store location from the key and refuses anything outsidecommitLog
the prefix, so a key is the whole of the authority the call carries.dag:log:
The log is a tree
exec:<id> the run
node:<id>:<seq> its nodes, in the order they opened
A node that delegated through carries the child run's id, and thert.sub
child is an ordinary run with nodes of its own. walks that linkexecutionTree
depth-first and returns the result flat, each row tagged with its — sodepth
a client renders the tree by indenting and nothing else. No parent index is
needed: the link is the node that made it.
Sequences are zero-padded in the key, because the KV store returns a prefix
range in lexicographic order and that order has to be the order the nodes ran.
The runtime pads to the same width when it composes the cache key; the two
widths are one contract.
Retention is a cap on runs ( by default), enforced every hundredth open.5000
The log is diagnostics, not an archive.
Trigger changes reach the runtime over the bus
Creating, changing or deleting a trigger publishes . Thedag.triggers.changed
runtime subscribes to that topic alongside the triggers' own, so an edit is live
for the next event instead of waiting out a polling interval. Publishing is best
effort — a bus that is down must not fail an operator's edit — and the runtime's
periodic refresh remains the backstop.
Direct module dependencies
- g-bus — to announce a trigger change
Solution membership
- Not included in a predefined solution
Source
modules/repositories/automation/rp-dag