Closes the payload loop for a resource-backed node: writes a combinator's output
row into the node's OWN resource (cell.meta.resource), keyed by the cell key.
This is what makes "the resource IS the node, and its rows ARE its payload" true
in the code, not just the docs. A reduce/join whose into returns a row and
that omits an explicit upsert: has its row written HERE — an Ash upsert into the
node's resource, with change-detection — so the common case needs no host write
callback, and writing into a different resource becomes the explicit deviation
(a custom upsert:), not the default.
The key attribute
The cell key (a string) maps to one resource attribute — the payload key. It
defaults to :key; a resource whose primary key is named otherwise declares
payload_key :flow_key in its reactive block. The row is written with that
attribute set to the cell key; a :key field on the row itself is dropped (it's
the cell key, not a payload column).
Change detection
upsert/5 reads the existing row first and compares the writable attributes; it
returns :changed only when a create or a real value change happened, so the
drain's parent-dirty only fires for genuine changes (a no-op recompute
stays a no-op). Requires an upsert action on the resource — by default the
action named :upsert with upsert?: true; override with payload_action.
Summary
Functions
RETIRE a vanished unit's row: the unit's input rows are gone, so the derived row must go too — a derived table whose whole point is that you query it cannot keep rows for units that no longer exist (a stale row is indistinguishable from a live one).
Upsert row into resource under cell_key (written to key_attr), via
action. Returns :changed (created, or a writable attr differs) or
:unchanged. row's :key field is dropped before writing.
Upsert an IDENTITY-KEYED row (a composite-primary-key node): the row carries
its identity fields, the upsert conflicts on the primary key (Ash's default
for upsert? true), and no key column exists — the cell key is the
identity's serialization, derived elsewhere. Returns :changed | :unchanged
with the same read-compare change detection as upsert/6, and the same
:fingerprint / :fingerprint_attribute options.
Functions
RETIRE a vanished unit's row: the unit's input rows are gone, so the derived row must go too — a derived table whose whole point is that you query it cannot keep rows for units that no longer exist (a stale row is indistinguishable from a live one).
keys are cell keys; for an identity-keyed node each is the identity's "|"
serialization, split back into its fields. Rows already absent are skipped, so
this is idempotent. Returns the keys whose row was actually destroyed.
Requires a destroy action (default :destroy); a resource without one raises
with the fix, since silently keeping the row would defeat the reconcile.
Upsert row into resource under cell_key (written to key_attr), via
action. Returns :changed (created, or a writable attr differs) or
:unchanged. row's :key field is dropped before writing.
Options
:fingerprint— compare ONE value instead of every attribute: a field list (hashed) or(row -> value). The value is written to:fingerprint_attribute(default:fingerprint), so the next call has something to compare against.This is what a SOURCE-FED LEAF needs. Comparing every attribute is right for a derived node, where every attribute is part of the result — but a leaf's row carries fields that move on every observation without the observation having changed: a
last_seen_atby definition, anetaga server may re-issue for identical bytes. Comparing those reports a change on every poll and re-runs the whole cascade.:fingerprint_attribute— where the value is stored (default:fingerprint).
Upsert an IDENTITY-KEYED row (a composite-primary-key node): the row carries
its identity fields, the upsert conflicts on the primary key (Ash's default
for upsert? true), and no key column exists — the cell key is the
identity's serialization, derived elsewhere. Returns :changed | :unchanged
with the same read-compare change detection as upsert/6, and the same
:fingerprint / :fingerprint_attribute options.