pub struct RocksDbStorageEngine { /* private fields */ }Implementations§
Source§impl RocksDbStorageEngine
impl RocksDbStorageEngine
pub fn open(path: impl AsRef<Path>) -> Result<Self>
pub fn put_node(&self, node: &Node) -> Result<()>
pub fn get_node(&self, id: NodeId) -> Result<Option<Node>>
pub fn put_edge(&self, edge: &Edge) -> Result<()>
pub fn get_edge(&self, id: EdgeId) -> Result<Option<Edge>>
pub fn delete_edge(&self, id: EdgeId) -> Result<()>
pub fn detach_delete_node(&self, id: NodeId) -> Result<()>
Sourcepub fn apply_batch(&self, mutations: &[GraphMutation]) -> Result<()>
pub fn apply_batch(&self, mutations: &[GraphMutation]) -> Result<()>
Apply a sequence of mutations as one atomic rocksdb write. Either every mutation lands or none does — no replica can observe a partial result, even across a process crash.
Reads performed during batch construction (existing labels for PutNode, edge metadata for DeleteEdge / DetachDeleteNode) hit the live store, not the in-flight batch. Read-your-writes across mutations in the same batch is not supported. The executor never generates such patterns: it produces fresh ids per row and dedupes mutated entities before flushing.
Sourcepub fn all_nodes(&self) -> Result<Vec<Node>>
pub fn all_nodes(&self) -> Result<Vec<Node>>
Walk every node in the store. Used for snapshot construction; not suitable as a query primitive since it materializes the full set.
Sourcepub fn all_edges(&self) -> Result<Vec<Edge>>
pub fn all_edges(&self) -> Result<Vec<Edge>>
Walk every edge in the store. Used for snapshot construction.
Sourcepub fn create_checkpoint(&self, path: impl AsRef<Path>) -> Result<()>
pub fn create_checkpoint(&self, path: impl AsRef<Path>) -> Result<()>
Produce a consistent point-in-time checkpoint of the store at
path. Wraps rocksdb’s Checkpoint API — on the same
filesystem the checkpoint is effectively free (hard links over
the underlying SST files); cross-filesystem it falls back to a
full copy. path must NOT already exist; rocksdb creates it.
Used by the Raft state machine’s streaming snapshot path: a
snapshot is built by checkpointing into a temp dir, packing
the checkpoint’s files into a length-prefixed archive, and
shipping the bytes. Skips the “Vec
Sourcepub fn clear_all(&self) -> Result<()>
pub fn clear_all(&self) -> Result<()>
Drop every key from every column family. Used by snapshot install to wipe local state before applying the leader’s snapshot. Cheap for empty / small stores; for large stores this is O(n).
pub fn all_node_ids(&self) -> Result<Vec<NodeId>>
pub fn outgoing(&self, source: NodeId) -> Result<Vec<(EdgeId, NodeId)>>
pub fn incoming(&self, target: NodeId) -> Result<Vec<(EdgeId, NodeId)>>
Sourcepub fn list_property_indexes(&self) -> Vec<PropertyIndexSpec>
pub fn list_property_indexes(&self) -> Vec<PropertyIndexSpec>
Snapshot the currently-registered property indexes. Cheap clone — specs are tiny (a label + a property key).
Sourcepub fn create_property_index(&self, label: &str, property: &str) -> Result<()>
pub fn create_property_index(&self, label: &str, property: &str) -> Result<()>
Declare a new (label, property) single-property equality
index and backfill it by scanning every node that currently
carries label. Idempotent: re-creating an already-registered
index is a no-op, matching how Neo4j’s CREATE INDEX IF NOT EXISTS behaves.
Backfill is done in the same WriteBatch as the meta insert,
so a crash mid-backfill leaves the store with either zero
entries (batch not yet written) or a fully-populated index
(batch committed). No partial-build state is ever visible.
Sourcepub fn create_property_index_composite(
&self,
label: &str,
properties: &[String],
) -> Result<()>
pub fn create_property_index_composite( &self, label: &str, properties: &[String], ) -> Result<()>
Composite form of [create_property_index]. Declares a
(label, properties) tuple index and backfills it by scanning
every node currently carrying label. A node that’s missing
any of the properties, or carries an unindexable value for
any of them, contributes no entry — composite indexes are
all-or-nothing on the tuple.
Single-property calls delegate here with a length-1 slice; the on-disk key format is byte-identical to the pre-composite layout in that case.
Sourcepub fn create_property_constraint(
&self,
name: Option<&str>,
scope: &ConstraintScope,
properties: &[String],
kind: PropertyConstraintKind,
if_not_exists: bool,
) -> Result<PropertyConstraintSpec>
pub fn create_property_constraint( &self, name: Option<&str>, scope: &ConstraintScope, properties: &[String], kind: PropertyConstraintKind, if_not_exists: bool, ) -> Result<PropertyConstraintSpec>
Declare a new property constraint. Behavior summary:
- The name is the user-facing identifier. When
nameisNone, a deterministic default (constraint_<label>_<prop>_<kind>) is generated soDROP CONSTRAINTcan still target it. - If the chosen name is already registered with the same
(label, property, kind), this is a no-op (the existing spec is returned). If the name collides on a different spec, returnsConstraintNameConflictunlessif_not_existsis set, in which case the existing spec is preserved and returned unchanged. - UNIQUE constraints implicitly require a backing property
index.
create_property_constraintcreates one on(label, property)if it isn’t already registered — matching Neo4j’s “a unique constraint also provides an index” contract. - Before committing, the method scans existing nodes to
verify the constraint is satisfied. Any violation aborts
the creation with
ConstraintViolation; the registry is left unchanged.
The meta write and the implicit-index write are batched into a
single WriteBatch so the on-disk state stays consistent
under crash.
Sourcepub fn drop_property_constraint(
&self,
name: &str,
if_exists: bool,
) -> Result<()>
pub fn drop_property_constraint( &self, name: &str, if_exists: bool, ) -> Result<()>
Tear down a constraint by name. When if_exists is true,
dropping a non-existent constraint is a no-op. Never drops the
backing index for UNIQUE — users who want it gone issue a
separate DROP INDEX.
Sourcepub fn list_property_constraints(&self) -> Vec<PropertyConstraintSpec>
pub fn list_property_constraints(&self) -> Vec<PropertyConstraintSpec>
Snapshot the currently-registered constraints. Cheap clone — every spec is a handful of small strings and an enum.
Sourcepub fn drop_property_index(&self, label: &str, property: &str) -> Result<()>
pub fn drop_property_index(&self, label: &str, property: &str) -> Result<()>
Tear down a property index: removes the meta entry and every
entry under the (label, prop) prefix. Idempotent: dropping a
non-existent index is a no-op. Atomic via one WriteBatch.
pub fn drop_property_index_composite( &self, label: &str, properties: &[String], ) -> Result<()>
Sourcepub fn list_edge_property_indexes(&self) -> Vec<EdgePropertyIndexSpec>
pub fn list_edge_property_indexes(&self) -> Vec<EdgePropertyIndexSpec>
Snapshot the currently-registered edge property indexes.
Mirror of RocksDbStorageEngine::list_property_indexes for
relationship scope. Cheap clone — specs are tiny.
Sourcepub fn create_edge_property_index(
&self,
edge_type: &str,
property: &str,
) -> Result<()>
pub fn create_edge_property_index( &self, edge_type: &str, property: &str, ) -> Result<()>
Declare a new (edge_type, property) edge property index and
backfill it by scanning every edge currently carrying
edge_type. Idempotent: re-creating an already-registered
index is a no-op. Mirror of
RocksDbStorageEngine::create_property_index for
relationship scope; the same “same WriteBatch” atomicity
guarantee applies — a crash mid-backfill leaves the store with
either zero entries or a fully-populated index, never a
partially-built one.
pub fn create_edge_property_index_composite( &self, edge_type: &str, properties: &[String], ) -> Result<()>
pub fn drop_edge_property_index( &self, edge_type: &str, property: &str, ) -> Result<()>
pub fn drop_edge_property_index_composite( &self, edge_type: &str, properties: &[String], ) -> Result<()>
pub fn list_point_indexes(&self) -> Vec<PointIndexSpec>
Sourcepub fn create_point_index(&self, label: &str, property: &str) -> Result<()>
pub fn create_point_index(&self, label: &str, property: &str) -> Result<()>
Declare a new point index on (label, property) and backfill
it by scanning every node that currently carries the label.
Idempotent: re-creating a registered index is a no-op. Same
“single WriteBatch” atomicity guarantee as the property-index
create path — a crash mid-backfill leaves the store with
either zero entries or a fully-populated index.
Sourcepub fn drop_point_index(&self, label: &str, property: &str) -> Result<()>
pub fn drop_point_index(&self, label: &str, property: &str) -> Result<()>
Tear down a point index. Removes the meta entry and every
entry under the <label>\0<prop>\0 prefix — that sweep
covers all SRID variants at once, since SRID is to the right
of the label+property header. Idempotent.
Sourcepub fn nodes_in_bbox(
&self,
label: &str,
property: &str,
srid: i32,
xlo: f64,
ylo: f64,
xhi: f64,
yhi: f64,
) -> Result<Vec<NodeId>>
pub fn nodes_in_bbox( &self, label: &str, property: &str, srid: i32, xlo: f64, ylo: f64, xhi: f64, yhi: f64, ) -> Result<Vec<NodeId>>
Range query: node ids whose indexed point falls inside the
axis-aligned bounding box [(xlo, ylo), (xhi, yhi)] under
srid. The scan walks the Morton-code cell range that
covers the bbox and applies a precise per-row filter against
the coordinates stored in the value — the cell range is a
superset, so the filter is what guarantees correctness.
A missing index returns empty rather than erroring, matching
the property-seek convention (lets the planner race a DROP
without a fatal). Results are sorted by NodeId for
determinism across runs.
pub fn list_edge_point_indexes(&self) -> Vec<EdgePointIndexSpec>
Sourcepub fn create_edge_point_index(
&self,
edge_type: &str,
property: &str,
) -> Result<()>
pub fn create_edge_point_index( &self, edge_type: &str, property: &str, ) -> Result<()>
Declare a new edge point index and backfill it by scanning
every edge currently carrying edge_type. Idempotent.
Single-property / relationship-scope analogue of
RocksDbStorageEngine::create_point_index.
Sourcepub fn drop_edge_point_index(
&self,
edge_type: &str,
property: &str,
) -> Result<()>
pub fn drop_edge_point_index( &self, edge_type: &str, property: &str, ) -> Result<()>
Tear down an edge point index. Idempotent.
Sourcepub fn edges_in_bbox(
&self,
edge_type: &str,
property: &str,
srid: i32,
xlo: f64,
ylo: f64,
xhi: f64,
yhi: f64,
) -> Result<Vec<EdgeId>>
pub fn edges_in_bbox( &self, edge_type: &str, property: &str, srid: i32, xlo: f64, ylo: f64, xhi: f64, yhi: f64, ) -> Result<Vec<EdgeId>>
Relationship-scope analogue of
RocksDbStorageEngine::nodes_in_bbox. Same Z-order cell
range + precise bbox filter via stored coords, just over
CF_EDGE_POINT_INDEX.
Sourcepub fn edges_by_property(
&self,
edge_type: &str,
property: &str,
value: &Property,
) -> Result<Vec<EdgeId>>
pub fn edges_by_property( &self, edge_type: &str, property: &str, value: &Property, ) -> Result<Vec<EdgeId>>
Look up edge ids for a (edge_type, property, value) equality
via the edge-property-index CF. Mirror of
RocksDbStorageEngine::nodes_by_property for relationship
scope; unindexable value types surface as
Error::UnindexableValue so callers can fall back to a
type-wide scan rather than silently returning empty.
Sourcepub fn edges_by_properties(
&self,
edge_type: &str,
properties: &[String],
values: &[Property],
) -> Result<Vec<EdgeId>>
pub fn edges_by_properties( &self, edge_type: &str, properties: &[String], values: &[Property], ) -> Result<Vec<EdgeId>>
Composite form of [edges_by_property]. Same contract as
RocksDbStorageEngine::nodes_by_properties on the
relationship side.
Sourcepub fn nodes_by_property(
&self,
label: &str,
property: &str,
value: &Property,
) -> Result<Vec<NodeId>>
pub fn nodes_by_property( &self, label: &str, property: &str, value: &Property, ) -> Result<Vec<NodeId>>
Look up node ids for a (label, property, value) equality via
the property index CF. The caller is responsible for checking
that the index exists first (e.g. the planner only emits
IndexSeek when it has verified via
RocksDbStorageEngine::list_property_indexes).
Unindexable value types (Float64, List, Map, Null) return
Error::UnindexableValue — callers should surface this to
the user rather than silently returning an empty result.
Sourcepub fn nodes_by_properties(
&self,
label: &str,
properties: &[String],
values: &[Property],
) -> Result<Vec<NodeId>>
pub fn nodes_by_properties( &self, label: &str, properties: &[String], values: &[Property], ) -> Result<Vec<NodeId>>
Composite form of [nodes_by_property]. Seeks every node
whose (label, property_i = value_i) tuple matches in order.
The two slices must have equal length. Unindexable value
types surface as Error::UnindexableValue the same way
the single-property form does — composite indexes are
all-or-nothing and an unindexable slot can never match a
stored tuple.
pub fn nodes_by_label(&self, label: &str) -> Result<Vec<NodeId>>
pub fn edges_by_type(&self, edge_type: &str) -> Result<Vec<EdgeId>>
Trait Implementations§
Source§impl StorageEngine for RocksDbStorageEngine
impl StorageEngine for RocksDbStorageEngine
fn put_node(&self, node: &Node) -> Result<()>
fn get_node(&self, id: NodeId) -> Result<Option<Node>>
fn detach_delete_node(&self, id: NodeId) -> Result<()>
fn put_edge(&self, edge: &Edge) -> Result<()>
fn get_edge(&self, id: EdgeId) -> Result<Option<Edge>>
fn delete_edge(&self, id: EdgeId) -> Result<()>
Source§fn apply_batch(&self, mutations: &[GraphMutation]) -> Result<()>
fn apply_batch(&self, mutations: &[GraphMutation]) -> Result<()>
GraphMutation for the variant set.fn all_nodes(&self) -> Result<Vec<Node>>
fn all_edges(&self) -> Result<Vec<Edge>>
fn all_node_ids(&self) -> Result<Vec<NodeId>>
fn outgoing(&self, source: NodeId) -> Result<Vec<(EdgeId, NodeId)>>
fn incoming(&self, target: NodeId) -> Result<Vec<(EdgeId, NodeId)>>
fn nodes_by_label(&self, label: &str) -> Result<Vec<NodeId>>
fn edges_by_type(&self, edge_type: &str) -> Result<Vec<EdgeId>>
fn nodes_by_property( &self, label: &str, property: &str, value: &Property, ) -> Result<Vec<NodeId>>
Source§fn nodes_by_properties(
&self,
label: &str,
properties: &[String],
values: &[Property],
) -> Result<Vec<NodeId>>
fn nodes_by_properties( &self, label: &str, properties: &[String], values: &[Property], ) -> Result<Vec<NodeId>>
Self::nodes_by_property. properties
and values are parallel slices of equal length — the tuple
must match an equivalent stored index entry. Single-property
callers should use Self::nodes_by_property which delegates
here with length-1 slices.Source§fn edges_by_property(
&self,
edge_type: &str,
property: &str,
value: &Property,
) -> Result<Vec<EdgeId>>
fn edges_by_property( &self, edge_type: &str, property: &str, value: &Property, ) -> Result<Vec<EdgeId>>
StorageEngine::nodes_by_property for the relationship side.
The caller is responsible for verifying the (edge_type, property)
index exists before dispatching — a missing index returns an
empty result (no entries maintained) rather than erroring so
the planner’s race with a concurrent DROP stays safe.fn create_property_index(&self, label: &str, property: &str) -> Result<()>
fn drop_property_index(&self, label: &str, property: &str) -> Result<()>
Source§fn create_property_index_composite(
&self,
label: &str,
properties: &[String],
) -> Result<()>
fn create_property_index_composite( &self, label: &str, properties: &[String], ) -> Result<()>
Self::create_property_index — declares
a tuple index over (label, properties...). Single-property
callers should use Self::create_property_index which
delegates here with a length-1 slice.Source§fn drop_property_index_composite(
&self,
label: &str,
properties: &[String],
) -> Result<()>
fn drop_property_index_composite( &self, label: &str, properties: &[String], ) -> Result<()>
Self::drop_property_index.fn list_property_indexes(&self) -> Vec<PropertyIndexSpec>
Source§fn create_edge_property_index(
&self,
edge_type: &str,
property: &str,
) -> Result<()>
fn create_edge_property_index( &self, edge_type: &str, property: &str, ) -> Result<()>
(edge_type, property) single-property equality
index and backfill it from every edge currently carrying the
type. Idempotent: re-creating an already-registered index is a
no-op. Matches StorageEngine::create_property_index for
node scope.Source§fn drop_edge_property_index(
&self,
edge_type: &str,
property: &str,
) -> Result<()>
fn drop_edge_property_index( &self, edge_type: &str, property: &str, ) -> Result<()>
(edge_type, prop) prefix. Idempotent.Source§fn create_edge_property_index_composite(
&self,
edge_type: &str,
properties: &[String],
) -> Result<()>
fn create_edge_property_index_composite( &self, edge_type: &str, properties: &[String], ) -> Result<()>
Self::create_edge_property_index.Source§fn drop_edge_property_index_composite(
&self,
edge_type: &str,
properties: &[String],
) -> Result<()>
fn drop_edge_property_index_composite( &self, edge_type: &str, properties: &[String], ) -> Result<()>
Self::drop_edge_property_index.Source§fn list_edge_property_indexes(&self) -> Vec<EdgePropertyIndexSpec>
fn list_edge_property_indexes(&self) -> Vec<EdgePropertyIndexSpec>
Source§fn create_point_index(&self, label: &str, property: &str) -> Result<()>
fn create_point_index(&self, label: &str, property: &str) -> Result<()>
(label, property) and backfill it
by scanning every node carrying label. Idempotent:
re-creating a registered index is a no-op. Single-property
and node-scope only — composite / relationship-scope spatial
indexes are follow-ups.Source§fn drop_point_index(&self, label: &str, property: &str) -> Result<()>
fn drop_point_index(&self, label: &str, property: &str) -> Result<()>
(label, property) header — the
SRID-keyed sub-prefixes all fall inside that header, so one
range scan covers all coordinate systems. Idempotent.Source§fn list_point_indexes(&self) -> Vec<PointIndexSpec>
fn list_point_indexes(&self) -> Vec<PointIndexSpec>
Source§fn nodes_in_bbox(
&self,
label: &str,
property: &str,
srid: i32,
xlo: f64,
ylo: f64,
xhi: f64,
yhi: f64,
) -> Result<Vec<NodeId>>
fn nodes_in_bbox( &self, label: &str, property: &str, srid: i32, xlo: f64, ylo: f64, xhi: f64, yhi: f64, ) -> Result<Vec<NodeId>>
(label, property) under srid. Entries tagged with a
different SRID are scoped out by the index key prefix, so
cross-SRID rows never leak. A missing index returns empty
rather than erroring — matches the soft-fail contract of
Self::nodes_by_property.Source§fn create_edge_point_index(&self, edge_type: &str, property: &str) -> Result<()>
fn create_edge_point_index(&self, edge_type: &str, property: &str) -> Result<()>
Self::create_point_index.
Declares an edge point index on (edge_type, property) and
backfills by scanning every edge currently carrying
edge_type. Idempotent.Source§fn drop_edge_point_index(&self, edge_type: &str, property: &str) -> Result<()>
fn drop_edge_point_index(&self, edge_type: &str, property: &str) -> Result<()>
Self::drop_point_index.Source§fn list_edge_point_indexes(&self) -> Vec<EdgePointIndexSpec>
fn list_edge_point_indexes(&self) -> Vec<EdgePointIndexSpec>
Source§fn edges_in_bbox(
&self,
edge_type: &str,
property: &str,
srid: i32,
xlo: f64,
ylo: f64,
xhi: f64,
yhi: f64,
) -> Result<Vec<EdgeId>>
fn edges_in_bbox( &self, edge_type: &str, property: &str, srid: i32, xlo: f64, ylo: f64, xhi: f64, yhi: f64, ) -> Result<Vec<EdgeId>>
Self::nodes_in_bbox.Source§fn create_property_constraint(
&self,
name: Option<&str>,
scope: &ConstraintScope,
properties: &[String],
kind: PropertyConstraintKind,
if_not_exists: bool,
) -> Result<PropertyConstraintSpec>
fn create_property_constraint( &self, name: Option<&str>, scope: &ConstraintScope, properties: &[String], kind: PropertyConstraintKind, if_not_exists: bool, ) -> Result<PropertyConstraintSpec>
name is None, the
backend derives a deterministic name from the other fields so
DROP CONSTRAINT can still target it. When if_not_exists is
true, re-declaring a constraint with the same name is a no-op
(including when the existing constraint has different
label/property/kind — matches Neo4j’s name-first semantics).
Returns the final PropertyConstraintSpec that was either
installed or already present. Read moreSource§fn drop_property_constraint(&self, name: &str, if_exists: bool) -> Result<()>
fn drop_property_constraint(&self, name: &str, if_exists: bool) -> Result<()>
name. When if_exists
is true, dropping a non-existent constraint is a no-op.Source§fn list_property_constraints(&self) -> Vec<PropertyConstraintSpec>
fn list_property_constraints(&self) -> Vec<PropertyConstraintSpec>
SHOW CONSTRAINTS output across restarts.Source§fn put_trigger(&self, name: &str, value: &[u8]) -> Result<()>
fn put_trigger(&self, name: &str, value: &[u8]) -> Result<()>
apoc.trigger.* registration. The value is a
JSON-encoded blob owned by meshdb-executor’s
TriggerSpec — storage stays format-agnostic so the
schema can evolve without bumping the storage trait.
Default impl errors loudly so backends that haven’t
wired triggers in yet surface the gap immediately.Source§fn delete_trigger(&self, name: &str) -> Result<()>
fn delete_trigger(&self, name: &str) -> Result<()>
Source§fn list_triggers(&self) -> Result<Vec<(String, Vec<u8>)>>
fn list_triggers(&self) -> Result<Vec<(String, Vec<u8>)>>
(name, value) pairs
in insertion-order by name. The value bytes are passed
straight back to the caller for format-side decoding.Source§fn create_checkpoint(&self, path: &Path) -> Result<()>
fn create_checkpoint(&self, path: &Path) -> Result<()>
path. The shape of what lands at path is backend-specific — the
caller is expected to package it in a backend-aware way (see
meshdb-rpc::raft_applier for the RocksDB path). For RocksDB this is
a Checkpoint directory of SST files; a different backend is free
to write a single file, a directory of segments, etc., as long as
its own open(path) can later rehydrate from the same layout.