ReactiveDag.Node.Recompute (reactive_dag v0.17.0-rc.40)

Copy Markdown View Source

THE engine: how a cell recomputes, decided by what its node DECLARED.

ReactiveDag.Drain calls this — there is nothing to configure and no strategy to pass. Dispatch reads the reactive block's own shape, in this order:

the node declaresthis runs
leaf? truepass the claimed keys through (its source wrote the rows)
reduceread the scoped slice of over, group, fold each group into a row
joinreconcile one input's two sides by key
per_keyone call per key, with the input-fingerprint skip
unionthe union of its inputs' keys
aggregatea datastore GROUP BY — no rows enter the BEAM
run :actiona generic Ash action on the node's own resource
compute Modthe escape hatch: a ReactiveDag.Op module
nothingpass through, LOUDLY (see below)

The combinators match BEFORE compute, so a node declaring both gets its combinator — which is why the verifier refuses that pair at compile time.

One engine

This was once one of two shipped strategies, passed in as recompute:, with a set-based sibling that dispatched cell.op to a SQL template from config. Both hosts ended up passing this one: what varied between them turned out to be DATA the DSL can declare — a module named in compute, a combinator, a key rule — not control flow. A pluggable engine everyone plugs the same thing into is an indirection, so the plug went and the DSL kept the declaring.

compute Mod remains the escape hatch for work Ash cannot express (an LLM call, a PDF parse). The distinction that matters: it is declared IN the node and checked by the verifier, rather than looked up in a config map.

A LEAF (or a cell with no compute) passes its claimed keys through as changed — a leaf's tuples were written by its source; if it reaches recompute at all, its claimed keys already ARE its changes.

Summary

Functions

Recompute keys of cell (or ["*"] for the whole cell), returning {:ok, changed} — the subset whose output actually changed. Only those propagate, which is what keeps a cascade O(real changes) rather than O(graph).

Functions

recompute(cell, keys)

Recompute keys of cell (or ["*"] for the whole cell), returning {:ok, changed} — the subset whose output actually changed. Only those propagate, which is what keeps a cascade O(real changes) rather than O(graph).

May return {:ok, changed, meta}: an arbitrary map the drain carries onto its %Report{} step without interpreting. Token counts, cache hits and retries are all just keys, so ReactiveDag.Insights and a dashboard can show what the work cost.

{:error, reason} is a CONTAINED failure — the drain rolls that cell back and carries on with the rest. It must be RETURNED, not raised: an exception inside a nested transaction aborts the outer one, so only a value can be isolated by a savepoint.