Computation Graph
A Computation Graph is an in-process, deterministic, event-driven DAG. Unlike a Workflow (where the task is the unit and state is persisted), a computation graph runs as a single traversal in memory, in response to an event. It is built from Nodes and is fired either by a Reactor or invoked directly from a workflow task.
Mental model
Section titled “Mental model”- A graph is a set of nodes wired by a topology (
a -> b). - Entry nodes read named sources from an
InputCache; processing nodes take their upstream node’s output; the terminal node (no downstream) produces the result. - It compiles to a callable function (
<name>_compiledin Rust) that the reactor invokes; the Boundary events an Accumulator emits are what populate the cache.
Interfaces
Section titled “Interfaces”A two-node pricing graph triggered by a reactor:
The #[computation_graph] macro declares the topology and generates the compiled
function; each pub async fn in the module is a node:
#[cloacina_macros::computation_graph( trigger = reactor("pricing_reactor"), graph = { ingest(orderbook) -> format_output, })]pub mod pricing { use super::*; pub async fn ingest(orderbook: Option<&OrderBookSnapshot>) -> SpreadSignal { /* ... */ } pub async fn format_output(input: &SpreadSignal) -> FormattedOutput { /* ... */ }}// generates pricing_compiled(&InputCache) -> GraphResultComputationGraphBuilder is a context manager; nodes are @cloaca.node
functions inside it, and the topology is a dict:
import cloaca
@cloaca.reactor(name="pricing_reactor", accumulators=["orderbook"], mode="when_any")class PricingReactor: pass
with cloaca.ComputationGraphBuilder( "pricing", reactor=PricingReactor, graph={ "ingest": {"inputs": ["orderbook"], "next": "format_output"}, "format_output": {}, # no `next` → terminal },) as g: @cloaca.node def ingest(orderbook): return spread_signal(orderbook)
@cloaca.node def format_output(ingest): return formatted(ingest)reactor= takes a @cloaca.reactor-decorated class, and each graph entry
is a dict (inputs list, plus next / routes, or empty for terminal). See
the topology dict schema
for the full format. Python node functions receive owned values (the boundary
copies them) where Rust takes references.
Key facts
Section titled “Key facts”- Reactor-triggered or trigger-less. A graph either subscribes to a reactor
(
trigger = reactor("…")) or is trigger-less and invoked inline from a workflow task. - Data is serialized. Values that cross node boundaries implement
Serialize/Deserialize; the cache stores bytes. - Deterministic. Given the same cache, the traversal is deterministic.
Build one
Section titled “Build one”- Embed it → Embed · Tutorials
- Ship it to a server → Run the Service · Tutorials
See also
Section titled “See also”- Node · Reactor · Accumulator · Boundary event