Skip to main content

RuleEngine

Struct RuleEngine 

Source
pub struct RuleEngine { /* private fields */ }

Implementations§

Source§

impl RuleEngine

Source

pub fn new() -> Self

Source

pub fn reset_chain_state(&mut self)

Reset the transient chaining state.

Called by db.rs from the same RAII guard that restores emit_deltas after every apply, so a panic unwinding out of a hook cannot leave chain_depth non-zero — which would make begin_chain compute active = false and silently disable chaining for the life of the engine. On the normal path this state is already clean and the call is a no-op.

Source

pub fn rules(&self) -> impl Iterator<Item = &RuleDef>

Source

pub fn chain_truncations(&self) -> u64

How many writes hit MAX_CHAIN_DEPTH with rule-relevant work still pending, since this engine was constructed. A non-zero value means some derived edges beyond the cap are stale: the store is not a fixpoint of its own rule set, and no single later write will repair it. Not persisted; replay re-runs the same hooks, so the value is re-derived identically on reopen.

Source

pub fn is_owned(&self, etype: u32, src: u32, dst: u32) -> bool

Source

pub fn provenance(&self) -> &BTreeMap<String, BTreeSet<(u32, u32, u32)>>

Read-only view of the provenance map: rule name → set of (etype_sym, src, dst).

Triggers a one-time lazy decode from retained bytes when called before the first mutation on a clean-open (no-WAL) store.

Source

pub fn provenance_touching( &self, node: u32, ) -> impl Iterator<Item = (&str, u32, u32, u32)> + '_

O(degree) reverse-index lookup: every provenance triple that touches node.

Dispatches to the lazy-decoded or live index depending on whether retained bytes have already been consumed by a mutation.

Source

pub fn provenance_touching_len(&self, node: u32) -> usize

Number of provenance triples incident on node.

Source

pub fn is_tripped(&self, name: &str) -> bool

One-way latch: true after a budget breach until Self::rebuild is the only exit (and only if the full desired set then fits).

Source

pub fn fire_count(&self, name: &str) -> u64

Evaluations of this rule: one tick per on_node_changed fire, and one tick per participating node on backfill and rebuild (even when rebuild is a provenance no-op).

Source

pub fn drain_deltas(&mut self) -> Vec<EngineEdgeDelta>

Drain and return all pending edge-fire / retract deltas since the last call. Callers (db.rs log_then_apply_with) invoke this after a successful WAL commit + apply to build [DbEvent]s for live subscriptions. [GraphDb::open_with] drains and discards after WAL replay so replay noise never leaks to subscribers.

§T2 note (as-of replay)

When Plan-15 T2 adds as-of replay for subscribers, that path should call apply-only (no log_then_apply_with) and then call drain_deltas() to feed those events to the replaying subscriber. The suppression is already in place: apply accumulates but never emits; drain_deltas is the only emission gate.

Source

pub fn pending_delta_count(&self) -> usize

Number of accumulated deltas not yet drained. Used by debug_assert in log_then_apply_with to catch stale-delta bugs.

Source

pub fn pending_deltas_since(&self, cursor: usize) -> &[EngineEdgeDelta]

Borrow the slice of deltas accumulated since cursor without consuming them. cursor should be the value returned by pending_delta_count() before an engine call.

The returned slice is valid until the next call to drain_deltas(). T1’s drain discipline is preserved: these deltas are still in the buffer and will be drained by log_then_apply_with after apply returns.

Source

pub fn to_persist( &self, ) -> (Vec<RuleDef>, BTreeMap<String, BTreeSet<(u32, u32, u32)>>, BTreeMap<String, bool>, BTreeMap<String, u64>)

Snapshot support: definitions + provenance + tripped/fires. Candidate indexes and the by_node reverse index are NOT included (derived: reindex_all / rebuild_by_node on open).

Source

pub fn from_persist( rules: Vec<RuleDef>, prov: BTreeMap<String, BTreeSet<(u32, u32, u32)>>, tripped: BTreeMap<String, bool>, fires: BTreeMap<String, u64>, ) -> Self

Reconstruct engine from a snapshot. Caller must call reindex_all after.

Source

pub fn set_emit_deltas(&mut self, emit: bool)

Enable or disable delta accumulation.

Set to true before the first subscriber or view is added. Set to false when the last subscriber and last view are removed. See the emit_deltas field doc for the safety invariant.

Source

pub fn emit_deltas(&self) -> bool

Whether delta accumulation is currently enabled.

Source

pub fn take_rebuild_needed(&mut self) -> Vec<String>

Drain rule names that exceeded the IVF dst-drift rebuild threshold during the most recent on_node_changed / on_node_removed.

Source

pub fn queue_rebuild_needed(&mut self, name: String)

Re-queue name so a later write can issue RebuildRule.

Used when auto-rebuild WAL IO fails after a durable user op.

Source

pub fn export_ivf_state(&self) -> BTreeMap<String, RuleIvfExport>

Export IVF state for all approximate rules. Passed to snapshot() in core-api and stored in the V4 snapshot so open() can restore cluster assignments without re-fitting k-means.

Source

pub fn reindex_all( &mut self, ids: &IdMap, syms: &Interner, labels: &[u32], props: ColumnsView<'_>, )

Rebuild all candidate indexes by scanning every node. Call on open.

Source

pub fn reindex_all_load_ivf( &mut self, ids: &IdMap, syms: &Interner, labels: &[u32], props: ColumnsView<'_>, ivf_state: BTreeMap<String, RuleIvfExport>, )

Like reindex_all but LOADS persisted IVF state for approximate rules instead of re-fitting k-means. This eliminates the cold-start re-fit cost when opening a V4 snapshot.

ivf_state: map from rule name to (src_export, dst_export) as produced by export_ivf_state / stored in the V4 snapshot.

For approximate rules absent from ivf_state (e.g. a rule added after the snapshot), falls back to fit_ivf_clusters.

Source

pub fn store_snapshot_state( &self, hnsw_blobs: BTreeMap<String, (Vec<u8>, Vec<u8>)>, ivf_bytes: Vec<u8>, )

Store HNSW blobs and raw IVF bytes from a snapshot without deserializing.

Called from restore_snapshot_state in db.rs. Neither the HNSW graphs nor the IVF centroids are materialized here; they are consumed lazily:

  • consume_retained_state_eager (WAL-present open, before WAL replay)
  • The mutation-hook lazy-init guard (clean open, first-write cost)
  • ensure_hnsw_loaded (first ANN query on a clean open)
Source

pub fn store_provenance_bytes(&self, bytes: Vec<u8>)

Store raw rkyv provenance bytes retained from a V8 snapshot.

Called from restore_v8_base in db.rs after open. Provenance is not decoded here; it is materialized lazily — either by the &self read path (ensure_provenance_loaded) for stats/explain, or by the &mut self write path (ensure_provenance_loaded_mut) on the first mutation.

Source

pub fn ensure_provenance_loaded(&self)

Populate lazy_provenance from retained bytes for &self read paths.

Uses OnceLock for exactly-once initialization. The retained bytes are NOT consumed here; ensure_provenance_loaded_mut still has access to them for the write path. After the first mutation, retained_provenance_bytes is None and callers switch to the live self.provenance field instead.

Source

pub fn ensure_provenance_loaded_mut(&mut self)

Decode and install retained provenance bytes into the live mutable fields.

No-op if bytes have already been consumed or were never stored. Must be called under &mut self before any operation that reads or diffs against self.provenance, self.owned, or self.by_node.

Source

pub fn consume_retained_state_eager( &mut self, ids: &IdMap, syms: &Interner, labels: &[u32], props: ColumnsView<'_>, )

Eagerly consume retained snapshot state before WAL replay.

Call this in open_with when the WAL has records. Runs the O(n) node scan + restores persisted IVF centroids and HNSW blobs so that WAL replay finds fully-populated indexes. Marks indexes_populated = true.

Source

pub fn ensure_hnsw_loaded(&self)

Deserialize retained HNSW blobs into lazy_hnsw for the clean-open ANN read path. Takes &self so it can be called from find_similar_vector and search_hybrid under a shared (db.read()) lock.

Uses OnceLock to guarantee exactly-once initialization even under concurrent shared access. The retained blobs are borrowed (not consumed) so that a subsequent first-mutation call to consume_retained_state_eager can still load the persisted HNSW graphs into self.indexes.

Called before the first ANN query on a clean-open (no WAL) store.

Source

pub fn indexes_populated(&self) -> bool

Returns true if candidate indexes have been built (either eagerly or via the lazy mutation-hook trigger).

Source

pub fn export_hnsw_state(&self) -> BTreeMap<String, (Vec<u8>, Vec<u8>)>

Export HNSW graphs for all approximate rules as opaque bincoded blobs.

Returns a map from rule name to (src_blob, dst_blob). An empty Vec means the corresponding side has no initialized HNSW graph.

Source

pub fn export_hnsw_state_passthrough( &self, ) -> BTreeMap<String, (Vec<u8>, Vec<u8>)>

Returns HNSW state for snapshotting. When indexes are not yet populated (clean open with no mutation), returns the retained raw blobs directly so that a migrate/snapshot does not silently drop fitted indexes.

Source

pub fn retained_ivf_bytes_clone(&self) -> Option<Vec<u8>>

Returns a clone of the retained raw IVF bincode bytes.

Returns None if no bytes are retained (fresh store or indexes already consumed by a mutation). Used by snapshot_with for passthrough when indexes have not yet been populated.

Source

pub fn load_hnsw_state(&mut self, blobs: BTreeMap<String, (Vec<u8>, Vec<u8>)>)

Restore HNSW graphs from bincoded blobs (overrides any incrementally built graphs produced during reindex_all_load_ivf).

Called from restore_snapshot_state in db.rs after reindex.

Source

pub fn hnsw_search_dst( &self, field: &str, dst_label: &str, q: &[f64], k: usize, ) -> Option<Vec<(u32, f64)>>

Find approximate nearest-neighbor ids on the dst side of the first approximate VectorSimilar rule covering (dst_label, field).

Returns None when no matching rule or HNSW index exists.

Source

pub fn hnsw_has_rule(&self, field: &str) -> bool

Returns true if any approximate VectorSimilar rule covers field.

Use as a capability probe before calling hnsw_search_dst or hnsw_search_any_dst — presence of the rule guarantees the native Rust path will be used (HNSW when the index is populated, Rust brute-force otherwise); it does NOT guarantee a populated HNSW index.

Source

pub fn hnsw_search_any_dst( &self, field: &str, q: &[f64], k: usize, ) -> Option<Vec<(u32, f64)>>

Like hnsw_search_dst but searches across all dst_labels that have an approximate VectorSimilar rule covering field.

Results from multiple rules are merged by node id (keeping the maximum score for any id that appears in more than one rule’s index), then sorted descending and truncated to k.

Returns None when no applicable rule has a populated HNSW index (same sentinel convention as hnsw_search_dst).

Source

pub fn create_rule( &mut self, def: RuleDef, g: &mut GraphMut<'_>, ) -> Result<(), String>

Register a rule and backfill existing nodes. Returns Err on failed validate() or duplicate name.

Source

pub fn delete_rule( &mut self, name: &str, g: &mut GraphMut<'_>, ) -> Result<(), String>

Remove the rule and exactly its owned edges. Returns Err if unknown.

Source

pub fn on_node_changed( &mut self, n: u32, changed: Option<(&str, Option<Value>)>, g: &mut GraphMut<'_>, )

Called when node n is inserted (changed=None) or a field is updated.

  • None: all rules where n’s label matches either side fire; index gains n.
  • Some((field, old_value)): only rules watching field fire; index is updated using old_value for removal so stale buckets are cleaned.

For via-hop rules (def.via_label.is_some()), also fires when n carries the via-label: finds all srcs that route through n and recomputes their derived edges. Via-hop rules bypass the candidate index and use compute_desired_via instead.

Derived edges written here are chained into via-hop rules before the call returns (see RuleEngine::chain_from), so one write reaches a bounded fixpoint.

Source

pub fn on_edge_changed( &mut self, etype_str: &str, src_id: u32, dst_id: u32, g: &mut GraphMut<'_>, )

Called when a user edge (etype_str, src_id, dst_id) is inserted or deleted (not a derived edge — those are managed by provenance, not here).

For any via-hop rule where via_edge == etype_str and src_id carries src_label, the src_id’s desired derived-edge set may have changed: a new WORKS_AT edge makes a new Org reachable as a via-node, and a deleted WORKS_AT removes a previously reachable Org.

This is the only hook the engine exposes for topology changes. It is called from db.rs on WalRecord::InsertEdge and WalRecord::DeleteEdge immediately after the topo is updated (so g.topo already reflects the new state), and re-entrantly by RuleEngine::chain_from for derived edges a rule just wrote.

Source

pub fn on_node_removed(&mut self, n: u32, g: &mut GraphMut<'_>)

Retract every provenance edge touching n across all rules and drop n from every rule index using its current props.

Caller must invoke this while labels/props are still intact (before tombstone). Rules are walked in BTree name order; touching edges in BTree triple order. A second call on an already-retracted node is a no-op (crash-window replay / absent state).

Retractions chain: a retracted derived edge that some via-hop rule hops over retracts that rule’s edges too, bounded by MAX_CHAIN_DEPTH.

Source

pub fn rebuild( &mut self, name: &str, g: &mut GraphMut<'_>, ) -> Result<(), String>

Recompute one rule from scratch. Only exit from the tripped latch.

If the full desired set fits in the budget, it is applied completely and tripped is cleared. If it still exceeds the budget, existing provenance is left completely untouched and tripped stays true (rebuild-is-noop for at/over-cap rules). Always counts as a fire evaluation per participating node. Returns Err if unknown.

A via-hop rule is never rebuilt through the candidate index — its predicate holds between the via node and the destination, which the index cannot express — so it goes through [apply_via_rebuild] or the via arm of [apply_streaming_rebuild_top_k] instead.

Trait Implementations§

Source§

impl Debug for RuleEngine

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RuleEngine

Source§

fn default() -> RuleEngine

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> ArchivePointee for T

Source§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
Source§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> LayoutRaw for T

Source§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
Source§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

Source§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
Source§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
Source§

impl<T> Pointee for T

Source§

type Metadata = ()

The metadata type for pointers and references to this type.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.