pub struct BatchDelta { /* private fields */ }Expand description
The per-source batch delta. Interior-RefCell so one Rc<BatchDelta> can be shared
between a TableSource, its forks, and the engine that drives the lifecycle
(plan D1 — the same idiom as cursors_open/fetch_error).
Implementations§
Source§impl BatchDelta
impl BatchDelta
Sourcepub fn new(primary_key: Vec<ColId>) -> BatchDelta
pub fn new(primary_key: Vec<ColId>) -> BatchDelta
An inactive delta over primary_key with its own private
DEFAULT_MAX_DELTA_BYTES budget. Every embedded caller gets one of these
implicitly and never pays for it; a host driving several tables replaces it with
one shared budget via set_budget.
Sourcepub fn with_budget(
primary_key: Vec<ColId>,
budget: Rc<DeltaBudget>,
) -> BatchDelta
pub fn with_budget( primary_key: Vec<ColId>, budget: Rc<DeltaBudget>, ) -> BatchDelta
An inactive delta charging against budget (D4) — pass the same Rc to every
table in a derivation and the ceiling bounds the whole transaction.
Sourcepub fn with_max_bytes(primary_key: Vec<ColId>, max_bytes: usize) -> BatchDelta
pub fn with_max_bytes(primary_key: Vec<ColId>, max_bytes: usize) -> BatchDelta
An inactive delta with a private budget of max_bytes — the single-table
convenience over with_budget.
Sourcepub fn set_budget(&self, budget: Rc<DeltaBudget>)
pub fn set_budget(&self, budget: Rc<DeltaBudget>)
Move this delta onto budget — how a host installs one shared ceiling across
every table it registers, on a delta the source already created (and may already
have forked: the budget lives behind the shared RefCell, so forks follow).
Anything currently charged moves with it.
Sourcepub fn budget(&self) -> Rc<DeltaBudget>
pub fn budget(&self) -> Rc<DeltaBudget>
The budget this delta charges against (shared — re-tune it with
DeltaBudget::set_max_bytes, read it with DeltaBudget::used).
Sourcepub fn used_bytes(&self) -> usize
pub fn used_bytes(&self) -> usize
Estimated bytes this delta is holding (its share of budget().used()).
Sourcepub fn begin(&self)
pub fn begin(&self)
Start a derivation: clear all state and mark active. Replaces the write path’s
BEGIN CONCURRENT throwaway transaction. Drops the secondary map entirely
(not just its rows) so M in the fold cost is bounded by the sorts actually
fetched during this transaction.
Sourcepub fn end(&self)
pub fn end(&self)
End a derivation: clear and deactivate. Replaces execute_batch("ROLLBACK") and
is strictly more reliable — there is no path on which a failed or skipped
rollback leaves derivation state behind.
pub fn is_active(&self) -> bool
pub fn is_empty(&self) -> bool
Sourcepub fn apply(&self, change: &SourceChange) -> Result<(), RindleError>
pub fn apply(&self, change: &SourceChange) -> Result<(), RindleError>
Fold one change into the delta — the stand-in for TableSource::write_change,
called from exactly where the write is called (after the fan-out drains), so the
I1/I2 push invariants hold by construction (design §3.1).
Erroring on an inactive delta is deliberate (plan §3.3): after the write closure
becomes delta.apply unconditionally, a push outside an open snapshot must fail
loudly rather than silently diverge.
A batch whose folded rows outgrow the shared DeltaBudget trips
[RindleError::DeltaOverflow] (design 306 D4): the delta drops its trees on the
spot — the budget is a memory bound, so overflowing must not keep holding the
rows — and every further apply re-raises the same error without accumulating.
The derivation for this batch is unrecoverable; the host sheds (commits the data,
tears down + re-hydrates the affected queries) rather than failing the write.
The charge is net: re-touching rows already in the delta costs nothing beyond the change in their content size, so a transaction that rewrites the same working set a million times never sheds — only one that holds an unbounded amount of it does.
An Add of a pk already present is [RindleError::ConsistencyViolation] in
every build (see Inner::add_row) — the loud fault the write-then-abort
path’s UNIQUE constraint used to raise, never a silent overwrite.
Sourcepub fn lookup_pk(&self, probe: &Row) -> DeltaLookup
pub fn lookup_pk(&self, probe: &Row) -> DeltaLookup
What the delta says about probe’s primary key. probe is any row carrying the
pk columns — the pk-sort comparator reads nothing else.
Sourcepub fn suppresses(&self, row: &Row) -> bool
pub fn suppresses(&self, row: &Row) -> bool
Whether a vended base row must be suppressed from the scan: its pk was touched, so its authoritative content lives in the delta (or nowhere). One pk-sort B-tree probe per vended base row — the merge’s per-row cost (design §5).
Sourcepub fn find_by_key(&self, key: &[(ColId, OwnedValue)]) -> Option<Row>
pub fn find_by_key(&self, key: &[(ColId, OwnedValue)]) -> Option<Row>
The delta row matching every (col, value) equality of key, if any — the
delta-first half of a get_row/lookup_unique point read (plan §4.4). key may
be any unique key, not just the pk, so this scans primary linearly
([constraint_matches] semantics, values_equal: null never matches) — bounded
by the delta, and these are build-time / consistency reads, not the hot path.
Sourcepub fn rows_for_fetch(
&self,
req: &FetchRequest,
sort: &Sort,
predicate: Option<&RowPredicate>,
) -> DeltaRows ⓘ
pub fn rows_for_fetch( &self, req: &FetchRequest, sort: &Sort, predicate: Option<&RowPredicate>, ) -> DeltaRows ⓘ
The delta side of one fetch (plan §4.2/§4.3): the rows this transaction’s
delta contributes to a request, narrowed exactly as compute_overlays narrows
the single-change overlay — constraint, multi-constraint any, predicate
(§2.1: delta rows, like overlay rows, are never seen by SQL, so predicate —
which can be strictly stronger than sql_condition — is the rule).
Lazy (plan §3.4 as amended): returns a DeltaRows pull stream over the
tree’s owning cursor rather than a materialized Vec, so a fetch costs one
O(log |delta|) seek plus the rows the merge actually consumes — never
O(|delta|) per fetch (the O(N²)-per-batch trap under per-change ordered
re-fetches). The RefCell borrow lives only inside this call; the cursor owns
its Rc<BNode> spine (OQ-1), so nothing of the borrow escapes, and a later
apply path-copies around any node a live stream pins.
Vended in connection-sort order (sort, honoring req.reverse): the seek
index is constraint columns ++ sort (§3.2), and restricted to the group an
equality constraint selects, that order is the connection sort — which is what
makes the output mergeable against SQL’s ORDER BY sort stream.
start is a seek optimization only; the exact At/After cut is re-applied
downstream by generate_with_start, exactly as the tiebreak path relies on.