pub struct Plan {
pub nodes: Vec<Node>,
pub constants: BTreeMap<OpId, Core>,
pub root: OpId,
pub state: OpId,
pub session: OpId,
pub presence: OpId,
pub awareness: OpId,
pub signals: Vec<(Arc<str>, OpId)>,
}Expand description
The view as a dataflow.
Nodes are in dependency order — every input’s index is less than its consumer’s — so the engine is one forward pass with no scheduling.
Fields§
§nodes: Vec<Node>§constants: BTreeMap<OpId, Core>Constants, in the same index space as nodes, for the ones whose op is Op::Const.
root: OpId§state: OpId§session: OpId§presence: OpId§awareness: OpId§signals: Vec<(Arc<str>, OpId)>The declared signals that survived as nodes, so a report can use the program’s own names.
Implementations§
Source§impl Plan
impl Plan
Sourcepub fn counts(&self) -> (usize, usize)
pub fn counts(&self) -> (usize, usize)
How many operators are maintained by delta, and how many are recomputed.
Sources and constants are neither: the accumulator, the session and a string literal are inputs to the dataflow rather than steps in it, and counting them as “recomputed” would make every program look worse than it is by a fixed amount.
Sourcepub fn reapplied_per_event(&self) -> Vec<OpId> ⓘ
pub fn reapplied_per_event(&self) -> Vec<OpId> ⓘ
The operators whose per-element function captured something that moves on every event, so the whole collection is reapplied whenever anything happens.
This is the defect docs/99
§99.3 found by sweeping the tree by hand: a loop body that reads the accumulator is a
different function after every event, so §23.13’s rebuild rule reapplies it to every
element — a nested-loop join with no index, invisible until the collection is large. The
operators of §99.9 remove it where the shape can be recognised, and §99.6’s rule for the
shape that cannot is “compile it the slow way and say so”, which is
Node::because.
Published so the sweep can be a standing property rather than a thing somebody re-runs.
It was re-run by hand three times, and the third time found a site that had arrived one
change after the second and been missed — the figure in the document was stale because the
tree had grown under it (docs/08 §8.5.6’s third decay
direction). incremental.rs::no_program_in_the_tree_reapplies_a_collection_per_event is
what re-runs it now.
A per-subscription capture is not this and is not returned: a function that captured the session is reapplied when a subscriber navigates, which is a route change rather than an event, and calling the two the same thing is what made the hand sweep hard to read.
The nodes that do not read the session: §5.3’s shared dataflow.
Sourcepub fn compile(placed: &Placed) -> Plan
pub fn compile(placed: &Placed) -> Plan
Compile the view of a sliced program, and fuse it.
Everything downstream — the engine, the read models, both reports — reads the fused plan,
so there is one plan a program has rather than two that could disagree.
Plan::unfused is what the differential gate compares against.
Sourcepub fn compile_with(placed: &Placed, relate: Relate) -> Plan
pub fn compile_with(placed: &Placed, relate: Relate) -> Plan
The same, with Relate said out loud.
Sourcepub fn of_query(tables: &[Arc<str>], body: &Core) -> Plan
pub fn of_query(tables: &[Arc<str>], body: &Core) -> Plan
A plan for one expression over collections the caller supplies — the read model’s SQL,
compiled into the operators a program’s view compiles to
(docs/99 §99.9 item 9).
tables names the fields of the record the engine is handed as its state: the ith of
them holds the ith table’s rows, and the expression reads it as Var(i). That is the whole
of the arrangement — a query has no session, no presence and no accumulator of its own, so
the one source a view already has is the one a query uses too, and no operator here is new.
The expression is built rather than written, so it carries no CoreKind::Global and
there is nothing for a definition table to answer; the decomposition is given an empty table. The
consequence worth stating is that a query is held to exactly the recognitions a program is:
crate::relate reads the loop and emits the Op::Join, the Op::ArrangeBy and the
Op::GroupBy, so a join in SQL and a for loop that looks something up are the same
operators with the same delta rules and not two implementations that agree.
Sourcepub fn of_query_with(tables: &[Arc<str>], body: &Core, relate: Relate) -> Plan
pub fn of_query_with(tables: &[Arc<str>], body: &Core, relate: Relate) -> Plan
The same, with Relate said out loud — which is what lets a gate measure both settings.
Sourcepub fn unfused(placed: &Placed) -> Plan
pub fn unfused(placed: &Placed) -> Plan
The plan as the decomposition produced it, before crate::fuse rewrites it.
Works from the graph rather than from crate::split::Roles::view, for the reason
docs/23 built the graph in the first place: the
sliced expression has already lost which signal each part came from, and a plan whose nodes
cannot be named is a plan no report can explain.
Sourcepub fn unfused_with(placed: &Placed, relate: Relate) -> Plan
pub fn unfused_with(placed: &Placed, relate: Relate) -> Plan
The same, with Relate said out loud.