pub struct Graph { /* private fields */ }Implementations§
Source§impl Graph
impl Graph
pub fn new() -> Graph
pub fn with_storage_factory(storage_factory: StorageFactory) -> Graph
Sourcepub fn set_validate_changes(&self, on: bool)
pub fn set_validate_changes(&self, on: bool)
Enable/disable strict change-consistency validation (WS02.2). Off by default.
When on, try_source_push returns a
RindleError::ConsistencyViolation (instead of a release-stripped
debug_assert!) on a malformed change stream.
Sourcepub fn validate_changes(&self) -> bool
pub fn validate_changes(&self) -> bool
Whether strict change validation is currently enabled.
Sourcepub fn set_push_deadline(&self, deadline: Option<Instant>)
pub fn set_push_deadline(&self, deadline: Option<Instant>)
Arm (or clear) the wall-clock deadline for the CURRENT push (FOLLOWER-LAG-SHED §6.6).
A host that bounds per-push derive time arms now + P around each push and clears it
after; on expiry the fan-out checkpoints park a
RindleError::PushDeadlineExceeded and stop iterating — the push surfaces Err
from try_source_push with torn operator state, which the
host discards (the same abort≙epoch-rehydrate contract a panic uses). Never arm this
on wasm: the expiry check calls Instant::now(), which traps there.
Sourcepub fn begin_recording(&mut self)
pub fn begin_recording(&mut self)
Begin capturing a PipelineManifest: subsequent Graph::add /
Graph::alloc_storage record the ids they mint. Bracket one build_pipeline
(+ its sink) with this and Graph::take_recording to get the exact id set to
hand Graph::destroy_pipeline. # Panics (debug) if recording is already on.
Sourcepub fn take_recording(&mut self) -> PipelineManifest
pub fn take_recording(&mut self) -> PipelineManifest
Stop recording and return the captured PipelineManifest (empty if recording
was not active).
Sourcepub fn alloc_storage(&mut self) -> StorageId
pub fn alloc_storage(&mut self) -> StorageId
Allocate a fresh scratch-state slot for a stateful operator and return its
StorageId (the builder, 08 §5, calls this and embeds the id in the
Take/Cap/Exists struct). On the client/test path a fresh
MemoryStorage is the namespace (10
§3.3 / §4.5): each slot is a disjoint keyspace because it is a distinct
store. On the SQLite server path, a graph constructed with
StorageFactory::sqlite vends an op_id-namespaced OpStorage instead
(10 §4.4).
Sourcepub fn storage_snapshot(&self) -> Vec<Vec<(Box<str>, StorageValue)>>
pub fn storage_snapshot(&self) -> Vec<Vec<(Box<str>, StorageValue)>>
Snapshot all operator scratch storage, one entry per StorageId in
allocation order, each a full ascending scan("") of (key, value) pairs.
The test-only sink-independence probe (spec 11 §3.1.1): two graphs built
from the same Ast allocate storage in the same order, so snapshot index
i denotes the same operator’s store in both — letting
run_push_test_ast_view assert
Catch-sink storage equals View-sink storage (operator state is identical
regardless of sink). StorageValue has no PartialEq, so the comparison is
spelled out at the testkit boundary.
Sourcepub fn storage_entries_of(&self, manifest: &PipelineManifest) -> usize
pub fn storage_entries_of(&self, manifest: &PipelineManifest) -> usize
The number of (key, value) entries in the operator scratch storage one
pipeline owns — the leak probe (take_partition_leak.rs’s assertion, and the
family unbind’s “no zombie slot” check of design 310 impl plan D5), keyed by the
PipelineManifest captured when the pipeline was built rather than by the
whole arena, so a graph hosting several pipelines can be probed one at a time.
Sourcepub fn storage_dump_of(
&self,
manifest: &PipelineManifest,
) -> Vec<(usize, String, String)>
pub fn storage_dump_of( &self, manifest: &PipelineManifest, ) -> Vec<(usize, String, String)>
Every (slot, key, value) of one pipeline’s operator scratch storage — the
diagnostic twin of storage_entries_of, for the
failure message when a leak probe finds entries it did not expect. slot indexes
the manifest’s storage list (a stable, build-order name for the operator’s store).
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
Total node slots in the arena, including tombstoned (freed) slots. A teardown does not shrink this; a subsequent build reuses freed slots, so this staying flat across a destroy+rebuild is the observable proof of slot reuse.
Sourcepub fn storage_count(&self) -> usize
pub fn storage_count(&self) -> usize
Total storage slots in the arena, including freed (cleared) ones (the
node_count analogue for StorageId).
Sourcepub fn destroy_pipeline(&mut self, manifest: &PipelineManifest)
pub fn destroy_pipeline(&mut self, manifest: &PipelineManifest)
Tear down one pipeline, identified by the PipelineManifest captured when it
was built (recording mode). Two passes:
- Disconnect every
SourceConnthe pipeline owns viaSource::destroy, which nulls that connection’s output edge. Because the push fan-out only drives connections whose output isSome(Graph::try_source_push), this orphans the whole subgraph from every future source push. The sharedSourceitself — pre-registered, outside the manifest — is untouched: its indexes and connection slots persist (§3.10). - Reclaim each storage slot (
clearits contents) and each node slot (replace the operator withOperator::Tombstone, dropping its heavy state: View tree, Collector buffers, compiled predicates, …). Both bump the slot’s generation and push it onto the free-list, so the slot is recycled by the nextadd/alloc_storagewhile every other pipeline’s ids stay valid.
Idempotency / safety: each slot’s current generation is asserted to match the
manifest before freeing, so a double teardown (or a corrupted manifest) is a
loud fail-fast rather than silent corruption. Runs under &mut self, so it can
only be called between transactions, never mid-push.
Known limitation (deferred): the shared source’s per-connection slot is
nulled (step 1) but not freed — ConnIds are not yet generational, so a
source’s conns Vec grows by one small record per teardown. Under sustained
build/destroy churn against a long-lived source this is a slow, bounded-per-query
leak (no correctness impact: the fan-out skips nulled connections). Reclaiming it
needs a generational ConnId + per-source free-list — a planned fast-follow.
Sourcepub fn add_source(&mut self, schema: SourceSchema, initial: Vec<Row>) -> NodeId
pub fn add_source(&mut self, schema: SourceSchema, initial: Vec<Row>) -> NodeId
Add an in-memory source. # Panics if a row’s width does not match the schema;
prefer try_add_source in production (WS02.6).
Sourcepub fn try_add_source(
&mut self,
schema: SourceSchema,
initial: Vec<Row>,
) -> Result<NodeId, RindleError>
pub fn try_add_source( &mut self, schema: SourceSchema, initial: Vec<Row>, ) -> Result<NodeId, RindleError>
Fallible add_source: validates ingest row widths and
returns a RindleError::SchemaViolation instead of panicking (WS02.4).
pub fn add_memory_source(&mut self, source: MemorySource) -> NodeId
Sourcepub fn add_dyn_source(&mut self, source: Box<dyn Source>) -> NodeId
pub fn add_dyn_source(&mut self, source: Box<dyn Source>) -> NodeId
Add an external Source erased behind a trait object — e.g. the SQLite
TableSource from the rindle-sqlite crate. The in-memory backend uses the
concrete add_source/add_memory_source.
Sourcepub fn remove_source(&mut self, source: NodeId) -> Result<(), RindleError>
pub fn remove_source(&mut self, source: NodeId) -> Result<(), RindleError>
Remove an in-memory source added by add_source /
try_add_source: tombstone its node and free the slot (bump
generation + free-list) — the single-node analogue of destroy_pipeline.
The inverse of add_source, for a synthetic table whose last reading query is gone
(AGGREGATE-SYNC-DESIGN.md §4). Errors with a RindleError if source is not an
in-memory source node, or if it still has a live connection — every pipeline reading it
must be torn down first (destroy_pipeline), which the JS backend guarantees by
refcounting readers.
Sourcepub fn connect(
&mut self,
source: NodeId,
sort: Option<Sort>,
filters: Option<ConnectionFilters>,
split_edit_keys: Vec<ColId>,
) -> NodeId
pub fn connect( &mut self, source: NodeId, sort: Option<Sort>, filters: Option<ConnectionFilters>, split_edit_keys: Vec<ColId>, ) -> NodeId
Create a new connection (output) on a source and return its SourceConn
node id. Self-joins call this twice on the same source.
Sourcepub fn add_join_slot(
&mut self,
parent: NodeId,
child: NodeId,
parent_key: Vec<ColId>,
child_key: Vec<ColId>,
rel_slot: RelId,
) -> NodeId
pub fn add_join_slot( &mut self, parent: NodeId, child: NodeId, parent_key: Vec<ColId>, child_key: Vec<ColId>, rel_slot: RelId, ) -> NodeId
Wire a hierarchical join with an already-resolved RelId slot. The builder
(08) resolves the relationship name against its query-local slot tree —
computed from the query AST, not the shared source schema’s declared
relationships (which a query can’t pre-declare for synthesized EXISTS-gate
aliases like comments_0) — and passes the slot directly, so the join is not
re-resolved against input_schema(parent).
Sourcepub fn add_flipped_join_slot(
&mut self,
parent: NodeId,
child: NodeId,
parent_key: Vec<ColId>,
child_key: Vec<ColId>,
rel_slot: RelId,
) -> NodeId
pub fn add_flipped_join_slot( &mut self, parent: NodeId, child: NodeId, parent_key: Vec<ColId>, child_key: Vec<ColId>, rel_slot: RelId, ) -> NodeId
The flipped, child-driven inner join with an already-resolved RelId
slot — the flipped analogue of Graph::add_join_slot, for the builder’s
query-local slot resolution. The flipped join outputs parent rows with the
child relationship attached; wire its parent/child inputs with
Graph::set_out_edge (JoinParent/JoinChild).
Sourcepub fn add_flipped_join_slot_with_chunk_size(
&mut self,
parent: NodeId,
child: NodeId,
parent_key: Vec<ColId>,
child_key: Vec<ColId>,
rel_slot: RelId,
chunk_size: usize,
) -> NodeId
pub fn add_flipped_join_slot_with_chunk_size( &mut self, parent: NodeId, child: NodeId, parent_key: Vec<ColId>, child_key: Vec<ColId>, rel_slot: RelId, chunk_size: usize, ) -> NodeId
Graph::add_flipped_join_slot with an explicit IN-batch chunk size — the
test seam analogous to the JS setMultiConstraintChunkSizeForTest
(flipped-join.ts:57). A small size forces the chunked fetch path (per-window
fetch + node-level k-way merge) so it can be diffed against the unchunked path.
Sourcepub fn add_view(&mut self, input: NodeId, schema: Schema) -> NodeId
pub fn add_view(&mut self, input: NodeId, schema: Schema) -> NodeId
Add a production View over input with the default
ResultType::Complete (the server/test default). For explicit with_ids or a
pending result type use Graph::add_view_with. The view shape is carried by
schema (a relationship is in-view iff its RelDef has a child schema).
Sourcepub fn add_view_with(
&mut self,
input: NodeId,
schema: Schema,
with_ids: bool,
result_type: ResultType,
) -> NodeId
pub fn add_view_with( &mut self, input: NodeId, schema: Schema, with_ids: bool, result_type: ResultType, ) -> NodeId
Add a production View with explicit with_ids and initial
ResultType. The view shape is carried by schema.
Sourcepub fn add_skip(&mut self, skip: Skip) -> NodeId
pub fn add_skip(&mut self, skip: Skip) -> NodeId
Add an out-of-file operator (the fan-out seam). The builder takes a
fully-constructed Skip, so growing Skip’s fields
never touches this method. Wire its downstream with Graph::set_output.
(Take/Cap/Exists/FlippedJoin/Union* get an identical add_*.)
Sourcepub fn add_take(&mut self, take: Take) -> NodeId
pub fn add_take(&mut self, take: Take) -> NodeId
Add a Take (the LIMIT operator). Like
Graph::add_skip, takes a fully-constructed value (the builder hands it a
StorageId from Graph::alloc_storage); wire its downstream with
Graph::set_output (terminal sink) or Graph::set_out_edge (feeding a
relationship join’s parent port).
Sourcepub fn add_cap(&mut self, cap: Cap) -> NodeId
pub fn add_cap(&mut self, cap: Cap) -> NodeId
Add a Cap (the unordered EXISTS-child limiter). Same
shape as Graph::add_take: a fully-constructed value carrying its
StorageId; wire its downstream with Graph::set_output /
Graph::set_out_edge.
Sourcepub fn add_reduce(&mut self, reduce: Reduce) -> NodeId
pub fn add_reduce(&mut self, reduce: Reduce) -> NodeId
Add a Reduce (an invertible aggregate, REDUCE-DESIGN.md).
Same shape as Graph::add_take: a fully-constructed value carrying its
StorageId. Unlike Take, Reduce reshapes its row (output is a synthetic
aggregate row), so it carries its own output schema. Wire its downstream with
Graph::set_output.
Sourcepub fn add_exists(&mut self, exists: Exists) -> NodeId
pub fn add_exists(&mut self, exists: Exists) -> NodeId
Add an Exists gate (a FilterChain link). Wire its
upstream FilterStart’s chain head to it (Graph::set_chain_head) and its
downstream FilterOutput with Graph::set_output.
pub fn add_collector(&mut self, input: NodeId) -> NodeId
Sourcepub fn add_change_sink(&mut self, input: NodeId) -> NodeId
pub fn add_change_sink(&mut self, input: NodeId) -> NodeId
Add a change-stream sink: a terminal sink that records the
fully-materialized CaughtChange tree of every
change pushed to it (nested relationships drained eagerly). Wire it with
set_sink_edge; drain per-transaction events with
take_sink_changes and get the initial hydration set
with try_hydrate_change_sink. Implemented as a
Collector with caught-capture enabled, so no new operator variant (and no
change to the dispatch/fetch/schema match arms) is required.
Sourcepub fn add_filter_start(&mut self, input: NodeId) -> NodeId
pub fn add_filter_start(&mut self, input: NodeId) -> NodeId
A FilterStart over input (the upstream normal Input). Wire its single
chain edge with Graph::set_chain_head.
Sourcepub fn add_filter_end(&mut self, start: NodeId) -> NodeId
pub fn add_filter_end(&mut self, start: NodeId) -> NodeId
A FilterEnd paired with start. Wire its downstream with
Graph::set_output.
Sourcepub fn add_fan_out(&mut self, input: NodeId) -> NodeId
pub fn add_fan_out(&mut self, input: NodeId) -> NodeId
A FanOut over input. Wire its branches + paired FanIn with
Graph::set_fan.
Sourcepub fn add_fan_in(&mut self, fan_out: NodeId) -> NodeId
pub fn add_fan_in(&mut self, fan_out: NodeId) -> NodeId
A FanIn paired with fan_out. Wire its post-fan continuation with
Graph::set_output.
Sourcepub fn add_union_fan_out(&mut self, input: NodeId) -> NodeId
pub fn add_union_fan_out(&mut self, input: NodeId) -> NodeId
A UnionFanOut over input (the node-level OR fan-out). Wire its branch
broadcast edges + paired UnionFanIn with Graph::set_union_fan.
Sourcepub fn add_union_fan_in(
&mut self,
fan_out: NodeId,
inputs: Vec<NodeId>,
branch_constraints: Vec<Constraint>,
schema: Schema,
) -> NodeId
pub fn add_union_fan_in( &mut self, fan_out: NodeId, inputs: Vec<NodeId>, branch_constraints: Vec<Constraint>, schema: Schema, ) -> NodeId
A UnionFanIn paired with fan_out over the branch tails inputs, carrying
the merged branch schema (it owns the output schema; the sort must be
defined) and the per-branch pushable constraints (parallel to inputs) the
fan-in merges into each branch fetch. Wire its post-fan continuation with
Graph::set_output.
Sourcepub fn add_filter(&mut self, input: NodeId, pred: CompiledPredicate) -> NodeId
pub fn add_filter(&mut self, input: NodeId, pred: CompiledPredicate) -> NodeId
A Filter link over input with predicate pred. Wire its single
FilterOutput with Graph::set_output.
Sourcepub fn add_filter_probe(&mut self, input: NodeId) -> NodeId
pub fn add_filter_probe(&mut self, input: NodeId) -> NodeId
A FilterProbe link over input (proof instrumentation; see the struct).
Sourcepub fn set_chain_head(&self, filter_start: NodeId, head: NodeId)
pub fn set_chain_head(&self, filter_start: NodeId, head: NodeId)
Wire a FilterStart’s single chain edge (#output, the chain head).
Sourcepub fn set_fan(&self, fan_out: NodeId, branches: Vec<NodeId>, fan_in: NodeId)
pub fn set_fan(&self, fan_out: NodeId, branches: Vec<NodeId>, fan_in: NodeId)
Wire a FanOut’s branch outputs and its paired FanIn.
Sourcepub fn set_union_fan(
&self,
fan_out: NodeId,
branches: Vec<OutEdge>,
fan_in: NodeId,
)
pub fn set_union_fan( &self, fan_out: NodeId, branches: Vec<OutEdge>, fan_in: NodeId, )
Wire a UnionFanOut’s branch broadcast edges (each branch head + the port to
push it on — JoinParent for a flipped branch, Single for a filter branch)
and its paired UnionFanIn.
Sourcepub fn set_conn_output(&self, conn: NodeId, edge: OutEdge)
pub fn set_conn_output(&self, conn: NodeId, edge: OutEdge)
Wire a source connection’s output edge (mirrors input.setOutput).
Sourcepub fn set_output(&self, op: NodeId, downstream: NodeId)
pub fn set_output(&self, op: NodeId, downstream: NodeId)
Wire a single-output operator’s downstream. Covers the join plus every
single-FilterOutput chassis link (FilterEnd, FanIn, Filter,
FilterProbe). FilterStart uses Graph::set_chain_head and FanOut
uses Graph::set_fan — those are not single-output.
Sourcepub fn set_sink_edge(&self, upstream: NodeId, sink: NodeId)
pub fn set_sink_edge(&self, upstream: NodeId, sink: NodeId)
Wire the final edge from a built pipeline’s last operator into a terminal
sink (a view, change sink, or testkit Catch) or a forwarding testkit Snitch.
Routes a
SourceConn through Graph::set_conn_output (a bare source → sink
pipeline) and any single-output operator — including a transparent tap —
through Graph::set_output. Used by the testkit runners after the build
closure returns the op feeding the sink.
Sourcepub fn set_out_edge(&self, upstream: NodeId, edge: OutEdge)
pub fn set_out_edge(&self, upstream: NodeId, edge: OutEdge)
Wire an upstream operator’s output edge with an explicit port — the
port-aware generalization of Graph::set_output (which always wires
Port::Single). The builder (08) uses this to chain joins: a
SourceConn routes through the source’s Graph::set_conn_output; a
Join sets its OutEdge cell directly. The port says how the
downstream receives — JoinParent when upstream is the next join’s
parent (sibling relationships), JoinChild when it is a nested child top.
Sourcepub fn output_port_capable(&self, node: NodeId) -> bool
pub fn output_port_capable(&self, node: NodeId) -> bool
True iff node can carry a port-bearing OutEdge via Graph::set_out_edge.
Covers the port-aware ops (SourceConn/Join/FlippedJoin/Skip/Take/Cap/
Reduce) and the filter-chain / union-fan tails FilterEnd/UnionFanIn, which
now carry a port-bearing OutEdge (Gap B) — so a related/sibling relationship join
can sit over a where sub-graph (an OR, a nested AND-OR, or a flipped EXISTS).
Reduce is here as the top of a relationship-aggregate child subtree (§9). FanIn
is excluded: it returns up the filter-chain stack, it is not a downstream-pushing tail.
Sourcepub fn wire_single(&self, upstream: NodeId, downstream: NodeId)
pub fn wire_single(&self, upstream: NodeId, downstream: NodeId)
Wire a Port::Single forward edge from upstream to downstream, tolerant of a
non-port-aware tail. A SourceConn routes through Graph::set_conn_output;
every other single-output operator — port-aware ops (via their Single fallback)
AND the filter-chain / union-fan tails FilterEnd/FanIn/UnionFanIn — routes
through Graph::set_output. Unlike Graph::set_out_edge, this accepts a
FilterEnd/UnionFanIn tail, so a Take/Cap (a root or EXISTS-child limit)
can sit above a where-subgraph / union-fan end without panicking.
Sourcepub fn memory_source(&self, id: NodeId) -> Option<&MemorySource>
pub fn memory_source(&self, id: NodeId) -> Option<&MemorySource>
The in-memory source behind id, or None if the node is a SourceLeaf::Dyn
backend (or not a source). The optimistic fork/rebase loop reaches through this
to fork the live primary tree (OPTIMISTIC-WRITES-DESIGN.md §1) — memory
sources only, by design: the loop is the wasm client’s, not a SQL backend’s.
pub fn cursors_open(&self, source: NodeId) -> i64
pub fn fetch<'g>( &'g self, id: NodeId, req: &FetchRequest, ) -> Box<dyn Iterator<Item = Node<'g>> + 'g>
Sourcepub fn try_fetch_all<'g>(
&'g self,
id: NodeId,
req: &FetchRequest,
) -> Result<Vec<Node<'g>>, RindleError>
pub fn try_fetch_all<'g>( &'g self, id: NodeId, req: &FetchRequest, ) -> Result<Vec<Node<'g>>, RindleError>
Eagerly drain fetch and surface any runtime error a backend parked during
the scan. The lazy Graph::fetch API remains infallible for iterator
composition; production callers that need error propagation should use this
owned-drain boundary.
Sourcepub fn set_join_precheck_bounds(
&self,
per_join: Option<usize>,
per_graph: usize,
)
pub fn set_join_precheck_bounds( &self, per_join: Option<usize>, per_graph: usize, )
Set the join membership pre-check’s bounds (design 311 §2.5 / §8): the per-join
distinct-key bound (None = off, the default) and the per-graph key budget.
Host-settable on a built &Graph like set_validate_changes.
A change resets every join’s set to Unbuilt (and the tracked total to 0): a
set built under a looser bound could be over the new one, and a set left Active
while the feature is off would silently go stale (maintenance stops with it) and
lie once it is turned back on. Rebuilding happens by observation on the next
unconstrained enumeration — never by a fetch of its own — so flipping the knob on
a live graph is safe at any time.
Sourcepub fn join_precheck_bounds(&self) -> JoinPrecheckBounds
pub fn join_precheck_bounds(&self) -> JoinPrecheckBounds
The current pre-check bounds.
Sourcepub fn join_precheck_stats(&self) -> JoinPrecheckStats
pub fn join_precheck_stats(&self) -> JoinPrecheckStats
The per-graph pre-check counters (probe hits/misses, disables by reason).
Sourcepub fn join_precheck_tracked_keys(&self) -> usize
pub fn join_precheck_tracked_keys(&self) -> usize
Distinct parent keys tracked across every join right now (the live charge
against the per_graph budget).
Sourcepub fn join_precheck_state(&self, join: NodeId) -> JoinPrecheckState
pub fn join_precheck_state(&self, join: NodeId) -> JoinPrecheckState
Inspect one join’s set. # Panics if join is not a live Join node.
Sourcepub fn join_precheck_states_of(
&self,
nodes: &[NodeId],
) -> Vec<JoinPrecheckState>
pub fn join_precheck_states_of( &self, nodes: &[NodeId], ) -> Vec<JoinPrecheckState>
The pre-check state of every live Join among nodes (a pipeline manifest’s node
list), in the order given; non-join and stale ids are skipped. The per-query
inspection hook a host with several pipelines on one graph uses (the replica
worker’s join_precheck_report). Inspection-only: linear in the graph’s joins per
manifest node.
Sourcepub fn join_precheck_states(&self) -> Vec<(NodeId, JoinPrecheckState)>
pub fn join_precheck_states(&self) -> Vec<(NodeId, JoinPrecheckState)>
Every live Join node with its pre-check state, in arena order — the inspection
hook for a pipeline built by build_pipeline (whose join ids are not handed back).
Sourcepub fn source_push(&self, src_id: NodeId, change: SourceChange)
pub fn source_push(&self, src_id: NodeId, change: SourceChange)
Infallible push (tests / prototyping).
§Panics
On any RindleError (SQLite I/O, a strict consistency violation, …). Prefer
try_source_push in production so the error is
handled, not aborted (WS02.6 fault model).
pub fn try_source_push( &self, src_id: NodeId, change: SourceChange, ) -> Result<(), RindleError>
Sourcepub fn source_push_isolated(
&self,
src_id: NodeId,
change: SourceChange,
) -> Result<(), RindleError>
pub fn source_push_isolated( &self, src_id: NodeId, change: SourceChange, ) -> Result<(), RindleError>
Server fault boundary (WS02.5): run one mutation under catch_unwind so a
residual — unconverted, genuinely-unexpected — panic during this push does
not abort the whole process (which, serving many connections, would take them
all down). A caught panic becomes a RindleError::Storage("internal panic: …").
Effective only under panic = "unwind" (the release-server profile, D2);
under panic = "abort" (the default/wasm client) a panic aborts regardless
and this just forwards the result. It is the last-resort net after the
WS02.1–02.4 conversions of the expected data-reachable panics — not a
substitute for them.
Recovery contract: fail the connection, do not retry in place. A panic
mid-mutation can leave operator/view state logically torn (a RefCell is
released on unwind — not poisoned, so the graph is still usable, just
possibly inconsistent). The owning connection MUST discard and re-hydrate the
view from source before serving further reads (WS09.6), never reuse it.
Sourcepub fn hydrate(&self, view_id: NodeId)
pub fn hydrate(&self, view_id: NodeId)
Build the initial view by draining the input pipeline (#hydrate,
array-view.ts:140). The fetch runs here (the Graph borrow stays with the
caller); the View folds each node InPlace and flushes
once. No RefCell borrow is held across the fetch (the root is taken out
inside hydrate_from, after this iterator is constructed).
Infallible hydrate (tests / prototyping).
§Panics
On any RindleError from the initial fetch. Prefer
try_hydrate in production (WS02.6 fault model).
pub fn try_hydrate(&self, view_id: NodeId) -> Result<(), RindleError>
Sourcepub fn flush_view(&self, view_id: NodeId)
pub fn flush_view(&self, view_id: NodeId)
Fire the view’s listeners with the current snapshot and close the
transaction (flush, array-view.ts:173).
pub fn take_runtime_error(&self) -> Result<(), RindleError>
Sourcepub fn view_add_listener(&self, view_id: NodeId, l: Listener) -> usize
pub fn view_add_listener(&self, view_id: NodeId, l: Listener) -> usize
Register a flush listener on the view; fires once immediately
(addListener, array-view.ts:115). Returns its index.
Sourcepub fn view_result_type(&self, view_id: NodeId) -> ResultType
pub fn view_result_type(&self, view_id: NodeId) -> ResultType
The view’s current ResultType.
Sourcepub fn set_view_result_type(&self, view_id: NodeId, rt: ResultType)
pub fn set_view_result_type(&self, view_id: NodeId, rt: ResultType)
Resolve a pending query’s result type (the async queryComplete path,
09 §3.8); fires listeners out of band.
Sourcepub fn set_collector_fetch_on_push(&self, id: NodeId, on: bool)
pub fn set_collector_fetch_on_push(&self, id: NodeId, on: bool)
Enable reentrant fetch-during-push capture on a collector.
Sourcepub fn collector_changes(&self, id: NodeId) -> Vec<CollectedChange>
pub fn collector_changes(&self, id: NodeId) -> Vec<CollectedChange>
The changes a collector has received, in order.
Sourcepub fn collector_fetched(&self, id: NodeId) -> Vec<Vec<Row>>
pub fn collector_fetched(&self, id: NodeId) -> Vec<Vec<Row>>
The row-sets captured by reentrant fetch-during-push (one per change).
Sourcepub fn take_sink_changes(&self, sink: NodeId) -> Vec<CaughtChange>
pub fn take_sink_changes(&self, sink: NodeId) -> Vec<CaughtChange>
Drain (take) the change events a change-sink (see add_change_sink)
has accumulated since the last call, in arrival order. The buffer is left empty,
so this is the per-transaction delta after a push batch + flush.
Sourcepub fn try_hydrate_change_sink(
&self,
sink: NodeId,
) -> Result<Vec<CaughtChange>, RindleError>
pub fn try_hydrate_change_sink( &self, sink: NodeId, ) -> Result<Vec<CaughtChange>, RindleError>
Materialize the pipeline feeding a change-sink as the initial set of
CaughtChange::Add events — the hydration
snapshot (one Add per top-level node, relationships drained). Does not touch
the push buffer. Mirrors Catch.fetch (testkit.rs) without the testkit gate.
Fallible like try_hydrate, and for the same reason: the
cold drain is a try_* boundary. A leaf error parked mid-fetch (an unsafe
integer, a failed sqlite3_step) ended the stream early, and folding reduce
groups during the drain can arm the §5.3 integer-sum overflow state (design
226) — an already-out-of-range set total must surface HERE as the typed error,
not register as a success whose aggregate cell is NULL while every later
write on the shared graph fails the overflow check. Callers tear the partial
pipeline down on Err (the registration error path), which also clears the
overflow state the drain armed.
Sourcepub fn try_hydrate_change_sink_with(
&self,
sink: NodeId,
req: &FetchRequest,
) -> Result<Vec<CaughtChange>, RindleError>
pub fn try_hydrate_change_sink_with( &self, sink: NodeId, req: &FetchRequest, ) -> Result<Vec<CaughtChange>, RindleError>
try_hydrate_change_sink under an explicit
FetchRequest — the constrained form a parameterized query family’s
per-partition hydrate uses (design 310 §4.4): the request’s constraint reaches
the leaf unchanged through the spine (joins forward it to the parent, Skip
keeps it, the root Take recognizes it as the partition and hydrates only that
partition’s window).
Sourcepub fn bind_family_partition(
&self,
fam: &FamilyPipeline,
sink: NodeId,
binding: &CanonKey,
) -> Result<Vec<CaughtChange>, RindleError>
pub fn bind_family_partition( &self, fam: &FamilyPipeline, sink: NodeId, binding: &CanonKey, ) -> Result<Vec<CaughtChange>, RindleError>
Bind one partition of a family (design 310 §4.4 “bind”): add binding to the
family’s binding set, then hydrate only that partition — a fetch of the
pipeline top constrained on the parameter columns, returned as the partition’s
initial Add set (the new subscriber’s snapshot). Existing partitions are
untouched. Errors if binding is already bound, or if the hydrate fails (the
binding is then rolled back, so the set never names a partition that was not
hydrated).
Sourcepub fn unbind_family_partition(
&self,
fam: &FamilyPipeline,
sink: NodeId,
binding: &CanonKey,
) -> Result<(), RindleError>
pub fn unbind_family_partition( &self, fam: &FamilyPipeline, sink: NodeId, binding: &CanonKey, ) -> Result<(), RindleError>
Unbind one partition of a family (design 310 §4.4 “unbind”, made concrete by impl plan D5 — a synthetic drain, not a slot delete). In order:
- fetch the partition’s current view rows through the pipeline top (bounded by the root limiter) while it is still bound;
- drop it from the binding set — the root connection now rejects the partition;
- inject
Change::Removefor each row at the spine tail’s output edge, above the root limiter and the gates the rows already passed, so everyrelatedjoin runs its ordinary parent-left cleanup (child limiter partitions, pre-check membership) and nothing is refilled from a connection that would now reject it; the sink’s caught output for these is discarded; - evict the spine EXISTS joins’ per-parent child partitions for those rows (the synthetic removes entered above them);
- evict the root
Take’s partition slot (it kept a size-0 slot, impl plan D4).
Nothing is emitted to surviving partitions. Cost O(partition rows) — the same order a singleton’s teardown pays.
Sourcepub fn hydrate_family(
&self,
fam: &FamilyPipeline,
sink: NodeId,
) -> Result<Vec<CaughtChange>, RindleError>
pub fn hydrate_family( &self, fam: &FamilyPipeline, sink: NodeId, ) -> Result<Vec<CaughtChange>, RindleError>
Every bound partition’s rows, concatenated in binding order — the family form
of a change-sink hydrate (read_snapshot).
Sourcepub fn push_at<'g>(&'g self, id: NodeId, change: Change<'g>, port: Port)
pub fn push_at<'g>(&'g self, id: NodeId, change: Change<'g>, port: Port)
Test/proof entry: inject a change at id (e.g. a FilterStart) exactly as
a SourceConn’s output edge would deliver it. Drives the filter sub-graph
in isolation; the full source push path is covered by the source tests.
Sourcepub fn probe_counts(&self, id: NodeId) -> (usize, usize, usize)
pub fn probe_counts(&self, id: NodeId) -> (usize, usize, usize)
(begin_filter, filter, end_filter) call counts recorded by a
FilterProbe — lets a test assert the lifecycle fired, and balanced.
Sourcepub fn view_data(&self, view_id: NodeId) -> ViewData
pub fn view_data(&self, view_id: NodeId) -> ViewData
The view’s current top-level result snapshot (the data getter). A cheap
Arc clone of the materialized tree. With the testkit feature,
testkit::view_data_to_caught converts it to a comparable crate::CaughtNode tree.
Sourcepub fn dump_view(&self, view_id: NodeId) -> Vec<(i64, Vec<i64>)>
pub fn dump_view(&self, view_id: NodeId) -> Vec<(i64, Vec<i64>)>
Test/inspection helper: [(pk_id, [child_pk_id, ...]), ...] using column 0.
Children are flattened across all relationship slots in slot order (the
production View’s dump_col0).
Sourcepub fn dump_view_deep(&self, view_id: NodeId) -> Vec<Col0Node>
pub fn dump_view_deep(&self, view_id: NodeId) -> Vec<Col0Node>
Recursive col-0 dump (the deep counterpart of Graph::dump_view): surfaces
grandchildren so a nested relationship (issue{comments{reactions}}) is fully
observable. See crate::view::View::dump_col0_deep.
Sourcepub fn dump_view_rows(&self, view_id: NodeId) -> Vec<(Vec<i64>, Vec<Vec<i64>>)>
pub fn dump_view_rows(&self, view_id: NodeId) -> Vec<(Vec<i64>, Vec<Vec<i64>>)>
Test helper: each top row’s FULL columns (as i64) + its children’s full
columns. Unlike Graph::dump_view (column-0 ids only) this surfaces
edited non-key columns, so an edit’s value change is observable. Assumes
all columns are Int.