pub struct EdgeStore {Show 20 fields
pub label: u32,
pub label_name: String,
pub src_label: u32,
pub dst_label: u32,
pub schema: EdgeSchema,
pub out_csr: CsrShardSet,
pub in_csr: CsrShardSet,
pub mvcc: MVCCManager,
pub properties: CsrWithProperties,
pub properties_dirty: bool,
pub is_open: bool,
pub next_edge_id: EdgeId,
pub config: EdgeTableConfig,
pub stats_manager: Option<Arc<StatsManager>>,
pub version_history: Arc<Mutex<LabelVersionHistory>>,
pub property_index_cache: HashMap<Arc<str>, usize>,
pub property_index: Option<EdgePropertyIndex>,
pub index_write_failures: u64,
pub index_consistency: IndexConsistency,
pub index_lag_baseline: u64,
/* private fields */
}Expand description
Node-group sharded edge store: one sharded CSR per direction with MVCC row timestamps.
Fields§
§label: u32§label_name: String§src_label: u32§dst_label: u32§schema: EdgeSchema§out_csr: CsrShardSet§in_csr: CsrShardSet§mvcc: MVCCManager§properties: CsrWithProperties§properties_dirty: boolWhether property columns changed since the last checkpoint.
is_open: bool§next_edge_id: EdgeId§config: EdgeTableConfig§stats_manager: Option<Arc<StatsManager>>§version_history: Arc<Mutex<LabelVersionHistory>>Version history tracking for schema changes
property_index_cache: HashMap<Arc<str>, usize>Cache for property name → schema index mapping to avoid O(n) linear lookups. Invalidated whenever schema changes.
property_index: Option<EdgePropertyIndex>Edge property index for efficient property-based filtering.
Best-effort asynchronous secondary structure, not a synchronous part
of the write: insert/delete failures are counted in
index_write_failures and reported to the metrics registry, never
failing the primary write. Primary data stays authoritative while the
index may lag; operators rebuild via build_property_index (or
rebuild_property_index_on_failures) once failures cross a chosen
threshold. A full streaming rebuild resets the lag baseline.
index_write_failures: u64Secondary index write failures since the last rebuild or reset. Observability only; primary data stays authoritative.
index_consistency: IndexConsistencyConsistency contract of the secondary index. Best-effort counts failures as lag; Strong fails the primary write on index error.
index_lag_baseline: u64Lag watermark: failure count at the last successful rebuild or reset. Queries compare the current counter against this baseline to decide whether the index is safe to use.
Implementations§
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn has_fragmented_group(&self, threshold: f32) -> bool
pub fn has_fragmented_group(&self, threshold: f32) -> bool
Whether any group is fragmented enough to justify a rebuild.
Scans each group with its own capacity/live ratio; the whole-table ratio stays an observation metric and never triggers collection. A fragmented clean group still triggers, so a dirty group cannot drag clean groups into the rebuild set.
Sourcepub fn compact_csr_only_with_watermarks(
&mut self,
watermarks: &MvccWatermarks,
margin: u64,
reserve_ratio: f32,
) -> Result<usize, StorageError>
pub fn compact_csr_only_with_watermarks( &mut self, watermarks: &MvccWatermarks, margin: u64, reserve_ratio: f32, ) -> Result<usize, StorageError>
Compact every group, sharing one pass cutoff across all sub-systems. Deletions are promoted into the tombstone layer. Explicit maintenance only; the write path uses the bounded reclaim pass below.
Fail-closed on authority drift like the load and freeze gates: a nonzero audit refuses the pass instead of compacting a diverged table into new files.
Sourcepub fn compact_reclaimable_vertices(
&mut self,
bound: u64,
max_vertices: usize,
) -> usize
pub fn compact_reclaimable_vertices( &mut self, bound: u64, max_vertices: usize, ) -> usize
Reclaim rows holding entries eligible at bound, visiting at most
max_vertices rows.
Groups without delete history are skipped without per-row inspection, so the pause stays proportional to the dirty groups rather than the size of the table. Each visited row pays a single fused dead/reclaimable probe instead of a census plus an eligibility walk. Frozen groups merge once per direction through the batched frozen path instead of one trailing memmove per row.
Sourcepub fn maybe_compact_for_flush_with_watermarks(
&mut self,
watermarks: &MvccWatermarks,
margin: u64,
threshold: f32,
)
pub fn maybe_compact_for_flush_with_watermarks( &mut self, watermarks: &MvccWatermarks, margin: u64, threshold: f32, )
Rebuild dense dirty regions before flush, sharing the pass cutoff. Clean regions are skipped, so a small dirty set never triggers a large rebuild pause.
Scope is tiered per region: rows holding reclaimable entries are the only trigger, density only widens the scope from row to region to group, and larger scopes require higher density. Row gaps kept at the packed density target never trigger a rebuild on their own; only tombstone dirt does.
Sourcepub fn compact_properties_with_watermarks(
&mut self,
watermarks: &MvccWatermarks,
margin: u64,
)
pub fn compact_properties_with_watermarks( &mut self, watermarks: &MvccWatermarks, margin: u64, )
Compact properties by reclaiming slots of unreferenced dead records.
Live rows keep their positions, so edge-to-row mappings never need remapping.
Reclaim authority tombstones below a global watermark.
Only entries whose topology rows are gone in both directions and whose property row is gone are removed, and only when the deletion timestamp is below the watermark-derived cutoff. The cutoff comes from the shared watermark capture, never from the table-local pin. A nonzero cross-copy audit fails closed so dangling references never lose their authority record.
pub fn compact_properties(&mut self, bound: u64)
Sourcepub fn deletion_stats(&self) -> DeletionStats
pub fn deletion_stats(&self) -> DeletionStats
Get deletion statistics for the sharded table.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn direction_usage_snapshot(&self) -> DirectionUsageSnapshot
pub fn direction_usage_snapshot(&self) -> DirectionUsageSnapshot
Current per-direction read counts for direction-narrowing audits.
Sourcepub fn form_profile_snapshot(&self) -> FormProfileSnapshot
pub fn form_profile_snapshot(&self) -> FormProfileSnapshot
Current width and access snapshot for record-form decisions.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn index_failure_count(&self) -> u64
pub fn index_failure_count(&self) -> u64
Secondary index write failures since the last rebuild or reset.
Sourcepub fn index_lag(&self) -> u64
pub fn index_lag(&self) -> u64
Lag since the last rebuild baseline. Zero means the index covers all writes since the baseline.
Sourcepub fn index_stale_secs(&self) -> u64
pub fn index_stale_secs(&self) -> u64
Seconds since the index first lagged; zero while current or disabled.
Sourcepub fn is_index_usable(&self) -> bool
pub fn is_index_usable(&self) -> bool
Whether the index is safe to serve candidate lookups.
Sourcepub fn index_status(&self) -> EdgeIndexStatus
pub fn index_status(&self) -> EdgeIndexStatus
One-call observability snapshot for monitoring polls.
Read-only. Poll this on a timer even without writes: the write-path
auto maintenance only runs on commits, so an idle table keeps its lag
until a background pass calls rebuild_index_if_needed.
Sourcepub fn emit_index_lag_metrics(&self)
pub fn emit_index_lag_metrics(&self)
Emit the current lag snapshot into the shared metrics registry.
Gauge caliber: overwrites the last values so idle polls keep them fresh. Call on a timer even without writes; the write path only refreshes these counters on commits and rebuilds.
Sourcepub fn report_index_status(&self) -> EdgeIndexStatus
pub fn report_index_status(&self) -> EdgeIndexStatus
Log-and-return observability report for the secondary index.
Read-only. Warns once per poll while lagged so a long BestEffort lag is noticeable; Strong mode still fails the primary write at write time and is reserved for small-cardinality critical attributes. Also refreshes the lag gauges so threshold alerts can poll metrics.
Sourcepub fn index_consistency(&self) -> IndexConsistency
pub fn index_consistency(&self) -> IndexConsistency
Consistency contract of the secondary index.
Sourcepub fn reset_index_failures(&mut self)
pub fn reset_index_failures(&mut self)
Reset the secondary index failure counter, typically after a rebuild. Also resets the lag baseline so the fresh index starts usable.
Sourcepub fn index_needs_rebuild(
&self,
failure_threshold: u64,
max_stale_secs: u64,
) -> bool
pub fn index_needs_rebuild( &self, failure_threshold: u64, max_stale_secs: u64, ) -> bool
Whether the secondary index needs a rebuild under the given policy.
True when lag reaches failure_threshold (zero disables the count
trigger) or when any lag persists longer than max_stale_secs
(zero disables the age trigger). No index means no rebuild.
Sourcepub fn rebuild_index_if_needed(
&mut self,
failure_threshold: u64,
max_stale_secs: u64,
) -> Result<bool, StorageError>
pub fn rebuild_index_if_needed( &mut self, failure_threshold: u64, max_stale_secs: u64, ) -> Result<bool, StorageError>
Rebuild the property index when the staleness policy demands it.
Returns true when a rebuild ran. Uses the capacity recorded at the last build so automatic maintenance needs no caller capacity. This is the shared rebuild entry called by write-path maintenance passes; idle tables with no writes need an external timer to invoke it, since no background thread is spawned inside the store. Refreshes the lag gauges on both paths so alerts clear together with the lag.
Sourcepub fn rebuild_property_index_on_failures(
&mut self,
threshold: u64,
pool_capacity: u64,
) -> Result<bool, StorageError>
pub fn rebuild_property_index_on_failures( &mut self, threshold: u64, pool_capacity: u64, ) -> Result<bool, StorageError>
Rebuild the property index when failures cross threshold.
Returns true when a rebuild ran. Threshold policy lives with the operator; the store only guarantees the counter is monotonic between resets. A rebuild scans all live edges, so the fresh index resets the lag baseline to the build’s own failure count.
Sourcepub fn enable_property_index(
&mut self,
pool_capacity: u64,
) -> Result<(), StorageError>
pub fn enable_property_index( &mut self, pool_capacity: u64, ) -> Result<(), StorageError>
Enable property index with the specified pool capacity. Builds the index from existing edge data.
Sourcepub fn has_property_index(&self) -> bool
pub fn has_property_index(&self) -> bool
Check if property index is enabled.
Sourcepub fn disable_property_index(&mut self)
pub fn disable_property_index(&mut self)
Drop the property index to free memory.
Sourcepub fn lookup_edges_by_property_range(
&self,
prop_name: &str,
value_lower: &[u8],
value_upper: &[u8],
) -> Vec<(u32, u32, i64)>
pub fn lookup_edges_by_property_range( &self, prop_name: &str, value_lower: &[u8], value_upper: &[u8], ) -> Vec<(u32, u32, i64)>
Lookup edges by a property value range using the EdgePropertyIndex.
Returns (src, dst, rank) tuples for matching edges.
Only serves while the index carries no lag since the last rebuild baseline; any lag yields an empty candidate set so callers fall back to the segment scan instead of risking dropped hits.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn vertex_fragmentation(&self, vid: u32, cutoff: u64) -> VertexFragmentation
pub fn vertex_fragmentation(&self, vid: u32, cutoff: u64) -> VertexFragmentation
Per-vertex fragmentation view combining both directions.
Observation only; the collection trigger consults vertex_census
and reclaimable_count directly.
Sourcepub fn storage_breakdown(&self) -> EdgeStorageBreakdown
pub fn storage_breakdown(&self) -> EdgeStorageBreakdown
Per-component storage breakdown for capacity planning.
Splits used_memory_size into its legs so operators can see the
Both topology doubling against the single-copy authority, property
and index shares before choosing a single direction or the bundled
inline form. Sums only, never walks rows; the total equals
used_memory_size.
Sourcepub fn topology_write_amplification(&self) -> u32
pub fn topology_write_amplification(&self) -> u32
Topology write amplification of the stored direction.
Both pays double topology writes and double topology storage to
serve forward and reverse traversal from local rows; single-direction
tables pay single. The authority, property and index shares stay
single-copy either way. Prefer OutOnly/InOnly at creation when
the missing direction is never traversed.
Sourcepub fn bytes_per_edge_breakdown(&self) -> (f64, f64, f64, f64)
pub fn bytes_per_edge_breakdown(&self) -> (f64, f64, f64, f64)
Bytes per live edge split into topology, authority, property mapping and row bookkeeping. Sums only; the denominator is live edges so an empty table reports zeros instead of dividing by zero.
Sourcepub fn suggest_direction_narrowing(
&self,
min_observations: u64,
) -> Option<DirectionNarrowingSuggestion>
pub fn suggest_direction_narrowing( &self, min_observations: u64, ) -> Option<DirectionNarrowingSuggestion>
Suggest narrowing a dual-direction table to one leg.
Usage governance (no engine change): when a Both table has served at
least min_observations directional reads and every one of them hit
the same leg, the other leg burns commit and storage bandwidth without
serving any traversal. Returns the single direction to keep with the
observed counts as the basis, or None when both legs serve traffic,
the table is already single-direction, or observations are too few to
decide. Counters are memory-only, so a freshly loaded table reports
zero until it serves traffic again.
Sourcepub fn migrate_storage_direction(
&mut self,
target: StorageDirection,
) -> Result<DirectionMigrationStats, StorageError>
pub fn migrate_storage_direction( &mut self, target: StorageDirection, ) -> Result<DirectionMigrationStats, StorageError>
Migrate a dual-direction table to one stored leg.
Usage-layer schema migration (no engine change): Both to OutOnly
drops the incoming leg, Both to InOnly drops the outgoing leg.
The remaining leg keeps every logical edge; the dropped leg answered
only reverse traversals, which read as empty afterwards (see
is_direction_available and direction_note). WAL redo stays
logical and replays onto the remaining leg, so unlike the record-form
switch no WAL fence or mandatory checkpoint gate applies; checkpoint
after the migration to reclaim the dropped leg storage. Widening
(single to Both) and single-to-single repurposing are rejected:
the dropped leg cannot be rebuilt from nothing here, reimport or
reload the table instead.
pub fn memory_size(&self) -> usize
pub fn used_memory_size(&self) -> usize
Sourcepub fn estimate_memory_usage(&self) -> usize
pub fn estimate_memory_usage(&self) -> usize
Estimate memory usage based on edge count and CSR strategy.
Write-path fast path: counts plus per-shard sizes only, never a full-table fragmentation walk. A zero per-edge measurement reports a zero estimate explicitly; backpressure treats zero as no pressure and never divides by the estimate.
Sourcepub fn check_and_apply_write_backpressure(&mut self, _current_ts: u64) -> bool
pub fn check_and_apply_write_backpressure(&mut self, _current_ts: u64) -> bool
Record mutable CSR pressure without performing maintenance on the write path.
pub fn needs_background_maintenance(&self) -> bool
Sourcepub fn maybe_run_auto_maintenance(&mut self) -> usize
pub fn maybe_run_auto_maintenance(&mut self) -> usize
Write-path fast path: bound comes from the per-table pin cache, with no global watermark capture. Background passes use the watermark variant below.
Sourcepub fn maintenance_skip_counts(&self) -> (u64, u64, u64)
pub fn maintenance_skip_counts(&self) -> (u64, u64, u64)
Maintenance skip counters for observability. All three retry on the next watermark-driven pass; growth without bound points at a stuck watermark or a permanently failing guard.
Sourcepub fn maybe_run_auto_maintenance_with_watermarks(
&mut self,
watermarks: &MvccWatermarks,
margin: u64,
) -> usize
pub fn maybe_run_auto_maintenance_with_watermarks( &mut self, watermarks: &MvccWatermarks, margin: u64, ) -> usize
Background variant: bound comes from one per-pass watermark capture shared across all tables. Refreshes the hot-path reuse hint from the same fresh capture before running, then reclaims authority tombstones whose physical rows are gone in both directions, so the authority map stays proportional to live edges rather than historical totals. A disabled sentinel clears reuse instead of running on an expired bound.
Sourcepub fn group_density(&self, gid: usize, outgoing: bool) -> f32
pub fn group_density(&self, gid: usize, outgoing: bool) -> f32
Density of one group on one leg: live edges over capacity. Observation only; the merge-scope selector owns the reclaim decision. Missing groups report full density so they never trigger compaction.
Sourcepub fn partition_inserts_by_owner(
&self,
srcs_dsts: &[(u32, u32)],
) -> Vec<(u32, Vec<usize>)>
pub fn partition_inserts_by_owner( &self, srcs_dsts: &[(u32, u32)], ) -> Vec<(u32, Vec<usize>)>
Partition insert keys by owner group so batch reservations and future parallel applies stay group-local. Pure routing helper: no state change.
Sourcepub fn group_write_count(&self, gid: u32) -> u64
pub fn group_write_count(&self, gid: u32) -> u64
Committed write count for one owner group, for hotspot observation.
Sourcepub fn hot_groups(&self, limit: usize) -> Vec<(u32, u64)>
pub fn hot_groups(&self, limit: usize) -> Vec<(u32, u64)>
Owner groups ordered by committed write volume, most-written first.
Observation only for hotspot diagnosis and future parallel-apply
scheduling; the write path stays table-serialized for correctness.
limit of zero returns every tracked group.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn matches_pushdown(
&self,
edge_id: EdgeId,
query_ts: u64,
predicates: &[ScanPredicate],
) -> bool
pub fn matches_pushdown( &self, edge_id: EdgeId, query_ts: u64, predicates: &[ScanPredicate], ) -> bool
Whether one edge matches every pushed predicate at query_ts.
Column-scan pushdown: only predicate columns are read through their null bitmaps, with no intermediate record materialization. Topology and attribute checks share the version authority: invisible edges never match even when their projected row still carries values.
Sourcepub fn matches_pushdown_assume_visible(
&self,
edge_id: EdgeId,
query_ts: u64,
predicates: &[ScanPredicate],
) -> bool
pub fn matches_pushdown_assume_visible( &self, edge_id: EdgeId, query_ts: u64, predicates: &[ScanPredicate], ) -> bool
Predicate match on an already-visible edge.
Counterpart of Self::matches_pushdown for callers holding a
prior authority verdict (scan iterators, batch filters): the column
check runs without a second visibility lookup.
Sourcepub fn filter_edge_ids(
&self,
predicates: &[ScanPredicate],
query_ts: u64,
candidates: Option<&[EdgeId]>,
) -> Vec<EdgeId>
pub fn filter_edge_ids( &self, predicates: &[ScanPredicate], query_ts: u64, candidates: Option<&[EdgeId]>, ) -> Vec<EdgeId>
Filter edge ids by pushed predicates at the column-scan layer.
Hits look up topology afterwards; misses never decode a record. Index first, segment statistics second, full walk last: an equality-or-range conjunction over lag-free indexed columns resolves to index candidates (verified back against the columns), otherwise whole owner groups provably excluding the predicates are skipped before the property walk. A full walk without either aid logs an observation so operators know an index would help.
Sourcepub fn segment_may_contain(
&self,
group: u32,
predicates: &[ScanPredicate],
) -> bool
pub fn segment_may_contain( &self, group: u32, predicates: &[ScanPredicate], ) -> bool
Whether one owner group may contain rows matching the predicates.
Dirty groups always scan: their uncheckpointed writes are not covered by the flushed statistics. Clean groups prune only on provable exclusion from the widened bounds, so the pre-filter never changes results.
Sourcepub fn segment_stats_snapshot(&self) -> HashMap<u32, GroupSegmentStats>
pub fn segment_stats_snapshot(&self) -> HashMap<u32, GroupSegmentStats>
Snapshot of the in-memory segment statistics.
Sourcepub fn topology_encoding_report(
&self,
) -> Vec<(String, TopologyColumnEncoding, usize, usize)>
pub fn topology_encoding_report( &self, ) -> Vec<(String, TopologyColumnEncoding, usize, usize)>
Encoding report for the persisted topology columns of both directions. Neighbor and edge-id columns come first, offset and length columns follow; all use the integer column path only.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn merged_out_nbrs_with_limit(
&self,
src: u32,
ts: u64,
limit: usize,
) -> Vec<Nbr>
pub fn merged_out_nbrs_with_limit( &self, src: u32, ts: u64, limit: usize, ) -> Vec<Nbr>
First-limit visible neighbors without decoding the full adjacency.
Uses the physical visit path so high-degree LIMIT queries stop
after k visible neighbors instead of decoding every edge plus properties.
Sourcepub fn merged_in_nbrs_with_limit(
&self,
dst: u32,
ts: u64,
limit: usize,
) -> Vec<Nbr>
pub fn merged_in_nbrs_with_limit( &self, dst: u32, ts: u64, limit: usize, ) -> Vec<Nbr>
First-limit visible in-neighbors, mirroring the out direction.
pub fn merged_out_nbrs_with_gate_limit( &self, src: u32, ts: u64, gate: &PendingGate<'_>, limit: usize, ) -> Vec<Nbr>
pub fn merged_in_nbrs_with_gate_limit( &self, dst: u32, ts: u64, gate: &PendingGate<'_>, limit: usize, ) -> Vec<Nbr>
pub fn out_edges_with_gate( &self, src: u32, ts: u64, gate: &PendingGate<'_>, ) -> Vec<EdgeRecord>
pub fn out_edges_with_gate_projected( &self, src: u32, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, ) -> Vec<EdgeRecord>
pub fn in_edges_with_gate( &self, dst: u32, ts: u64, gate: &PendingGate<'_>, ) -> Vec<EdgeRecord>
pub fn in_edges_with_gate_projected( &self, dst: u32, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, ) -> Vec<EdgeRecord>
pub fn out_edges_with_gate_projected_limit( &self, src: u32, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, limit: usize, ) -> Vec<EdgeRecord>
pub fn in_edges_with_gate_projected_limit( &self, dst: u32, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, limit: usize, ) -> Vec<EdgeRecord>
Sourcepub fn edge_id_of(
&self,
src: u32,
dst: u32,
rank: i64,
ts: u64,
) -> Option<EdgeId>
pub fn edge_id_of( &self, src: u32, dst: u32, rank: i64, ts: u64, ) -> Option<EdgeId>
Resolve the edge id for (src, dst, rank) without decoding properties.
Operation-layer point lookups use it to recheck the fetched record
through the pending-aware gate
(MVCCManager::is_edge_visible_with_gate). Single-direction tables
without the out leg probe the stored in leg at the destination row,
mirroring the delete path, instead of reporting a missing edge.
Sourcepub fn get_edge(
&self,
src: u32,
dst: u32,
rank: i64,
ts: u64,
) -> Option<EdgeRecord>
pub fn get_edge( &self, src: u32, dst: u32, rank: i64, ts: u64, ) -> Option<EdgeRecord>
Point lookup resolving through the stored leg.
Mirrors edge_id_of: tables without the out leg answer from the in
leg so single-direction tables stay queryable instead of silent.
Sourcepub fn get_edge_with_id(
&self,
src: u32,
dst: u32,
rank: i64,
ts: u64,
) -> Option<(EdgeRecord, EdgeId)>
pub fn get_edge_with_id( &self, src: u32, dst: u32, rank: i64, ts: u64, ) -> Option<(EdgeRecord, EdgeId)>
Point lookup resolving through the stored leg, paired with the edge id.
Single fused entry for operation-layer point lookups: one physical
row scan plus the authoritative visibility verdict inside
merged_get_edge, with the property projection reusing that verdict
instead of deciding visibility a second time. Callers needing a
pending-aware recheck reuse the returned id instead of scanning the
row again through edge_id_of.
pub fn get_edge_with_gate( &self, src: u32, dst: u32, rank: i64, ts: u64, gate: &PendingGate<'_>, ) -> Option<EdgeRecord>
pub fn get_edge_with_gate_projected( &self, src: u32, dst: u32, rank: i64, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, ) -> Option<EdgeRecord>
Sourcepub fn get_edge_projected_with_id(
&self,
src: u32,
dst: u32,
rank: i64,
ts: u64,
gate: &PendingGate<'_>,
projection: Option<&[Arc<str>]>,
) -> Option<(EdgeRecord, EdgeId)>
pub fn get_edge_projected_with_id( &self, src: u32, dst: u32, rank: i64, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, ) -> Option<(EdgeRecord, EdgeId)>
Pending-aware point lookup paired with the edge id.
Fused counterpart of get_edge_with_id for gate-carrying callers:
one physical row scan through merged_get_edge_with_gate, with the
projection reusing the gate verdict instead of deciding visibility a
second time. Operation-layer rechecks reuse the returned id for the
authority gate and the deletion-stamp probe instead of scanning the
row again.
pub fn out_edges(&self, src: u32, ts: u64) -> Vec<EdgeRecord>
pub fn out_edges_projected( &self, src: u32, ts: u64, projection: Option<&[Arc<str>]>, ) -> Vec<EdgeRecord>
Sourcepub fn merged_out_nbrs(&self, src: u32, ts: u64) -> Vec<Nbr>
pub fn merged_out_nbrs(&self, src: u32, ts: u64) -> Vec<Nbr>
Raw out-edge neighbors of src (MVCC-filtered, snapshot-consistent)
with no property decoding.
Owned single-row convenience over Self::fill_visible_into.
Record-building paths stream hot-only without this intermediate, and
high-frequency traversals use visit_out_with_gate or the batch
accessor instead.
pub fn in_edges(&self, dst: u32, ts: u64) -> Vec<EdgeRecord>
pub fn in_edges_projected( &self, dst: u32, ts: u64, projection: Option<&[Arc<str>]>, ) -> Vec<EdgeRecord>
Sourcepub fn batch_accessor(
&self,
outgoing: bool,
ts: u64,
) -> AdjacencyBatchAccessor<'_>
pub fn batch_accessor( &self, outgoing: bool, ts: u64, ) -> AdjacencyBatchAccessor<'_>
Batch adjacency accessor bound to one snapshot.
Traversal and batch queries use the accessor with a caller buffer so peak memory stays proportional to the batch size instead of the row degree. The buffer is valid only for the snapshot the accessor was created with and must not be held across snapshots.
pub fn has_edge(&self, src: u32, dst: u32, rank: i64, ts: u64) -> bool
Sourcepub fn edge_count(&self) -> u64
pub fn edge_count(&self) -> u64
Live edge count on the stored leg: the out leg when stored, else the in leg. Single-direction tables report their one leg, never zero.
Physical counter for maintenance and storage stats: it ignores the
read timestamp, so snapshot reads must use EdgeStore::visible_edge_count.
Sourcepub fn visible_edge_count(&self, ts: u64, gate: &PendingGate<'_>) -> u64
pub fn visible_edge_count(&self, ts: u64, gate: &PendingGate<'_>) -> u64
Visible edge count on the stored leg at ts: same leg selection as
EdgeStore::edge_count with the visibility gate applied per edge.
Counts without materializing records; snapshot reads stay consistent
with scans at the cost of a full-leg walk.
pub fn delta_edge_count(&self) -> u64
Sourcepub fn table_cardinality_at(
&self,
ts: u64,
gate: &PendingGate<'_>,
) -> TableCardinalitySnapshot
pub fn table_cardinality_at( &self, ts: u64, gate: &PendingGate<'_>, ) -> TableCardinalitySnapshot
Snapshot cardinality for optimizer costing at ts.
Live rows use the same visibility gate as scans so the estimate matches what queries observe; allocated slots add the cached tombstone count so tables with heavy deletes cost their physical footprint. Tombstones are not timestamp-filtered, so uncommitted deletes may widen the hole slightly; the result stays conservative for sizing. Shard-inconsistent reads: sizing and costing only, never a strongly consistent census.
pub fn scan(&self, ts: u64) -> Vec<EdgeRecord>
pub fn scan_projected( &self, ts: u64, projection: Option<Vec<Arc<str>>>, ) -> Vec<EdgeRecord>
Sourcepub fn scan_with_gate(&self, ts: u64, gate: &PendingGate<'_>) -> Vec<EdgeRecord>
pub fn scan_with_gate(&self, ts: u64, gate: &PendingGate<'_>) -> Vec<EdgeRecord>
Batch full-table scan with a pending gate. Materializes every visible
record, so it is an offline path for index builds and scatter-gather
queries; latency-sensitive traversals should use the streaming
EdgeStore::iter plus per-vertex limit pushdown instead.
Scans the stored leg: tables without the out leg iterate the in leg with swapped endpoint assembly so single-direction tables stay scannable instead of silent.
pub fn scan_with_gate_projected( &self, ts: u64, gate: &PendingGate<'_>, projection: Option<&[Arc<str>]>, ) -> Vec<EdgeRecord>
Sourcepub fn column_stats_snapshot(&self, column: &str) -> Option<ColumnStatsSnapshot>
pub fn column_stats_snapshot(&self, column: &str) -> Option<ColumnStatsSnapshot>
Optimizer-facing statistics snapshot for one property column (zone-map aggregated, with live row count).
pub fn iter(&self, ts: u64) -> EdgeTableScanIterator<'_> ⓘ
pub fn iter_projected( &self, ts: u64, projection: Option<Vec<Arc<str>>>, ) -> EdgeTableScanIterator<'_> ⓘ
Sourcepub fn has_edge_with_batch(
&self,
batch: &EdgeStagingBatch,
src: u32,
dst: u32,
rank: i64,
ts: u64,
) -> bool
pub fn has_edge_with_batch( &self, batch: &EdgeStagingBatch, src: u32, dst: u32, rank: i64, ts: u64, ) -> bool
Owner-view existence check overlaying a caller-owned staging batch.
Default reads stay on the committed snapshot for isolation; this helper lets the batch owner observe read-your-writes without touching committed state. Empty batches delegate to the fast path.
Sourcepub fn get_edge_with_batch(
&self,
batch: &EdgeStagingBatch,
src: u32,
dst: u32,
rank: i64,
ts: u64,
) -> Option<EdgeRecord>
pub fn get_edge_with_batch( &self, batch: &EdgeStagingBatch, src: u32, dst: u32, rank: i64, ts: u64, ) -> Option<EdgeRecord>
Owner-view point lookup overlaying a caller-owned staging batch.
Net inserts synthesize a record from staged properties (uncommitted edges own no edge id yet); net deletes hide the committed edge.
Sourcepub fn out_edges_with_batch(
&self,
src: u32,
ts: u64,
batch: &EdgeStagingBatch,
) -> Vec<EdgeRecord>
pub fn out_edges_with_batch( &self, src: u32, ts: u64, batch: &EdgeStagingBatch, ) -> Vec<EdgeRecord>
Owner-view out adjacency overlaying a caller-owned staging batch.
Sourcepub fn in_edges_with_batch(
&self,
dst: u32,
ts: u64,
batch: &EdgeStagingBatch,
) -> Vec<EdgeRecord>
pub fn in_edges_with_batch( &self, dst: u32, ts: u64, batch: &EdgeStagingBatch, ) -> Vec<EdgeRecord>
Owner-view in adjacency overlaying a caller-owned staging batch.
Source§impl EdgeStore
impl EdgeStore
pub fn flush<P>( &mut self, path: P, compression: CompressionType, ) -> Result<EdgeCheckpointKind, StorageError>
pub fn load<P>(&mut self, path: P) -> Result<(), StorageError>
Sourcepub fn diagnose_edge_wal_at<P>(
path: P,
) -> Result<EdgeWalDiagnosis, StorageError>
pub fn diagnose_edge_wal_at<P>( path: P, ) -> Result<EdgeWalDiagnosis, StorageError>
Read-only WAL diagnosis for an offline table directory.
Never mutates the log. Reports the last valid entry boundary and the salvageable operation count. The load path stays fail-closed; run this after a torn-tail load failure, then trigger repair explicitly.
Sourcepub fn repair_edge_wal_at<P>(path: P) -> Result<usize, StorageError>
pub fn repair_edge_wal_at<P>(path: P) -> Result<usize, StorageError>
Explicit offline WAL repair for a table directory.
Truncates the log at the last valid entry and returns the salvaged operation count. Only run while no writer holds the table, under the same single-writer discipline as every other mutation. Never called by the load path.
Sourcepub fn repair_edge_wal_at_reported<P>(
path: P,
) -> Result<EdgeWalRepairReport, StorageError>
pub fn repair_edge_wal_at_reported<P>( path: P, ) -> Result<EdgeWalRepairReport, StorageError>
Explicit offline WAL repair with a full before/after report.
Same fail-closed contract as Self::repair_edge_wal_at: the load
path defaults to reject, only this explicit call truncates, and the
returned report carries the salvaged prefix plus the discarded tail
byte counts with diagnostic logs on both sides.
Sourcepub fn load_with_wal_recovery<P>(
&mut self,
path: P,
mode: EdgeWalRecoveryMode,
) -> Result<Option<EdgeWalRepairReport>, StorageError>
pub fn load_with_wal_recovery<P>( &mut self, path: P, mode: EdgeWalRecoveryMode, ) -> Result<Option<EdgeWalRepairReport>, StorageError>
Load a checkpoint with an explicit WAL torn-tail policy.
Strict behaves exactly like Self::load. TruncateTornTail
runs the same explicit repair Self::repair_edge_wal_at_reported
performs before loading, and returns its report so the discarded
tail stays observable instead of silent. Repair still requires sole
ownership of the table directory.
Sourcepub fn load_for_unattended_restart<P>(
&mut self,
path: P,
) -> Result<Option<EdgeWalRepairReport>, StorageError>
pub fn load_for_unattended_restart<P>( &mut self, path: P, ) -> Result<Option<EdgeWalRepairReport>, StorageError>
Unattended single-node restart entry: truncates a torn WAL tail (if any), loads, and logs the repair report for audit ingestion.
Server restart paths without an operator call this instead of
Self::load. The report is logged at warn level when a repair ran
and returned to the caller for audit storage; Strict behavior is
unchanged for attended opens.
Sourcepub fn loaded_copy_mismatches(&self) -> (usize, usize)
pub fn loaded_copy_mismatches(&self) -> (usize, usize)
Orphan property mappings plus orphan CSR rows. Audit-only (load path plus tests); a nonzero count means corrupt files or a write-path regression.
Live-authority orphans (a live authority entry with no CSR row) are
reported separately by EdgeStore::live_authority_orphans and also
reject the load. The single-slot form rejects conflicting overwrites,
so any such orphan indicates a regression, never an expected overwrite.
Live authority entries with no CSR row in either direction. Audit-only (load path plus tests); a nonzero count means a past silent overwrite orphaned the authority record.
Sourcepub fn audit_copy_drift(&self) -> Vec<String>
pub fn audit_copy_drift(&self) -> Vec<String>
Authority-versus-projection drift report.
Single audit entry for the version-truth contract: the authority map
owns every creation/deletion stamp, CSR cold halves and property row
stamps are projections for collection only. Walks both CSR directions
plus the property mappings and reports every divergence as a string:
orphan rows/mappings, live authority orphans, and per-edge
delete_ts (or deleted-state) mismatches between a projection and
the authority. Empty means no drift. Load and reclaim paths fail
closed on any nonzero presence subset; this report adds the stamp
comparison so maintenance-time drift fails loudly instead of
continuing silently.
Sourcepub fn audit_and_report(&self) -> Vec<String>
pub fn audit_and_report(&self) -> Vec<String>
On-demand authority-versus-projection audit with metric emission.
Runs the same EdgeStore::audit_copy_drift report the load,
freeze and reclaim paths enforce, and additionally emits the three
orphan counters through the table metrics registry so drift is
observable when operators trigger it. Read-only: never mutates table
state. Callers are manual diagnostics and tests; maintenance passes
only refresh lag gauges and never run this full-table walk on a timer.
Source§impl EdgeStore
impl EdgeStore
pub fn add_property( &mut self, name: String, data_type: DataType, nullable: bool, ) -> Result<(), StorageError>
Sourcepub fn encode_property_columns(&mut self) -> usize
pub fn encode_property_columns(&mut self) -> usize
Select and apply encodings for every property column.
Explicit maintenance operation: hot columns stay unencoded between runs by design so everyday writes never pay re-encoding. Encodings change the physical representation without changing logical values, and the encoding choice is part of the checkpoint payload, so this marks properties dirty to carry the choice to the next checkpoint. Returns the number of columns that received an encoding.
Sourcepub fn refresh_property_stats(&mut self)
pub fn refresh_property_stats(&mut self)
Recompute persisted per-column statistics from current contents.
Sourcepub fn rebuild_schema_change_from_redo(
&mut self,
details: ChangeDetails,
) -> Result<(), StorageError>
pub fn rebuild_schema_change_from_redo( &mut self, details: ChangeDetails, ) -> Result<(), StorageError>
Rebuild schema change record during WAL recovery
This is used during recovery when the column already exists (from SchemaManager), but we need to update version_history to reflect the schema operation in the WAL. Does NOT add the property (it already exists), but DOES record the change.
pub fn remove_property(&mut self, name: &str) -> Result<(), StorageError>
pub fn rename_property( &mut self, old_name: &str, new_name: &str, ) -> Result<(), StorageError>
Sourcepub fn property_fallback_rewrites(&self) -> u64
pub fn property_fallback_rewrites(&self) -> u64
Table-level property fallback rewrites since creation.
Counts checkpoints where property dirt carried no group trace and every owner rewrote as insurance. Flat under normal load; growth points at a missing write-time mark.
Source§impl EdgeStore
impl EdgeStore
pub fn new(schema: EdgeSchema) -> Result<EdgeStore, StorageError>
pub fn with_config( schema: EdgeSchema, config: EdgeTableConfig, ) -> Result<EdgeStore, StorageError>
pub fn set_stats_manager(&mut self, stats: Arc<StatsManager>)
pub fn label(&self) -> u32
pub fn src_label(&self) -> u32
pub fn dst_label(&self) -> u32
pub fn schema(&self) -> &EdgeSchema
Sourcepub fn storage_direction(&self) -> StorageDirection
pub fn storage_direction(&self) -> StorageDirection
Storage direction derived from the enabled CSR strategies.
Sourcepub fn has_out_edges(&self) -> bool
pub fn has_out_edges(&self) -> bool
Whether the outgoing leg is stored and served.
Sourcepub fn out_multiplicity(&self) -> EdgeMultiplicity
pub fn out_multiplicity(&self) -> EdgeMultiplicity
Cardinality contract of the outgoing direction.
Sourcepub fn in_multiplicity(&self) -> EdgeMultiplicity
pub fn in_multiplicity(&self) -> EdgeMultiplicity
Cardinality contract of the incoming direction.
Sourcepub fn is_direction_available(&self, outgoing: bool) -> bool
pub fn is_direction_available(&self, outgoing: bool) -> bool
Whether a direction is available for reads. Missing legs read as empty adjacency instead of failing.
Sourcepub fn direction_note(&self, outgoing: bool) -> Option<String>
pub fn direction_note(&self, outgoing: bool) -> Option<String>
Human-readable reason when a direction is not stored. Returns None
while the direction is available. Query planning uses this to tell an
empty missing leg apart from a genuinely empty stored leg.
Sourcepub fn can_accept_rank(&self, rank: i64) -> bool
pub fn can_accept_rank(&self, rank: i64) -> bool
Whether this table may accept a write carrying the given rank.
Caller-side guard for bundled candidates: inline forms (Pure and
Bundled) carry no rank column, so any nonzero rank must stay on the
columnar form. Probe this before staging rank writes instead of paying
a whole-batch rollback after prevalidation rejects them with
BUNDLED_RANK_REQUIRES_COLUMNAR_MSG.
Sourcepub fn set_schema(&mut self, schema: EdgeSchema)
pub fn set_schema(&mut self, schema: EdgeSchema)
Replace the table schema and rebuild the property index cache.
Load-only helper: the caller must validate the resolved strategy-plus-form combination afterwards, since this setter alone performs no cardinality check.
Sourcepub fn version_history_ref(&self) -> Arc<Mutex<LabelVersionHistory>> ⓘ
pub fn version_history_ref(&self) -> Arc<Mutex<LabelVersionHistory>> ⓘ
Get reference to version history Arc for shared access
Source§impl EdgeStore
impl EdgeStore
pub fn delete_edge( &mut self, src: u32, dst: u32, rank: i64, ts: u64, ) -> Result<bool, StorageError>
Sourcepub fn delete_edges_batch(
&mut self,
keys: &[(u32, u32, i64)],
ts: u64,
) -> Result<usize, StorageError>
pub fn delete_edges_batch( &mut self, keys: &[(u32, u32, i64)], ts: u64, ) -> Result<usize, StorageError>
Delete many edges by key in a single staging commit.
One prevalidation and one commit carry the whole batch with the same
all-or-nothing semantics as repeated delete_edge calls: entries
apply in slice order and a mid-batch failure rolls the applied prefix
back with the table untouched afterwards. Keys never created report
no error and consume no tombstone, mirroring the single-delete
no-op; re-deleting an already-deleted edge fails the batch like the
single path. Returns the number of net applied deletes.
Sourcepub fn delete_incident_edges_of_vertex(
&mut self,
out_row: Option<u32>,
in_row: Option<u32>,
ts: u64,
) -> Result<Vec<IncidentDeletedEdge>, StorageError>
pub fn delete_incident_edges_of_vertex( &mut self, out_row: Option<u32>, in_row: Option<u32>, ts: u64, ) -> Result<Vec<IncidentDeletedEdge>, StorageError>
Delete every authority-live incident edge of one vertex in one batch.
out_row addresses the vertex on the stored out leg and in_row on
the stored in leg; the two address spaces differ when the endpoint
labels do, so the caller resolves each side separately and passes
None for a side holding nothing. Missing legs read as empty, never
as an error.
Collection sees exactly what per-edge cascade deletes would: edges
visible through the version authority at ts. One prevalidation,
one topology pass and one commit carry the whole fanout, reusing the
dual-leg consistency and prefix-rollback semantics of the regular
delete path. A frozen group holding any collected edge fails the
whole batch closed with the table untouched. The returned edges
carry their pre-delete property snapshot for engine-level undo and
index maintenance, ordered by edge id.
Sourcepub fn delete_incident_edges_of_vertices(
&mut self,
vertex_rows: &[(Option<u32>, Option<u32>)],
ts: u64,
) -> Result<Vec<IncidentDeletedEdge>, StorageError>
pub fn delete_incident_edges_of_vertices( &mut self, vertex_rows: &[(Option<u32>, Option<u32>)], ts: u64, ) -> Result<Vec<IncidentDeletedEdge>, StorageError>
Batch-delete all incident edges of multiple vertices in one staging
commit. Each (out_row, in_row) pair identifies one vertex’s rows in
the two CSR directions. Edges deduplicated by edge ID across the
entire batch, so bidirectional edges and shared endpoints are deleted
exactly once. Returns edges ordered by edge ID.
Sourcepub fn erase_edge(&mut self, src: u32, dst: u32, rank: i64, ts: u64) -> bool
pub fn erase_edge(&mut self, src: u32, dst: u32, rank: i64, ts: u64) -> bool
Physically erase an edge inserted by an uncommitted transaction.
Insert-undo path: unlike a user delete (logical deletion through
delete_edge), aborting an insert must leave no trace in any of the
three copies — otherwise the aborted edge would stay visible inside
its snapshot window and leave a permanent tombstone. Every step
tolerates absence, so replaying the undo (abort re-drive, WAL
recovery re-application) is idempotent.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn update_edge_property(
&mut self,
src: u32,
dst: u32,
rank: i64,
prop_name: &str,
value: &Value,
ts: u64,
) -> Result<bool, StorageError>
pub fn update_edge_property( &mut self, src: u32, dst: u32, rank: i64, prop_name: &str, value: &Value, ts: u64, ) -> Result<bool, StorageError>
Single property point write with its own log entry.
Own atomic unit outside topology batches: one WAL append plus one column write. Topology visibility still comes from the version authority; the column version chain only carries values.
pub fn update_edge_property_by_key( &mut self, params: UpdateEdgePropertyByKeyParams, ) -> Result<bool, StorageError>
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn revert_delete_edge(
&mut self,
src: u32,
dst: u32,
rank: i64,
ts: u64,
) -> Result<bool, StorageError>
pub fn revert_delete_edge( &mut self, src: u32, dst: u32, rank: i64, ts: u64, ) -> Result<bool, StorageError>
Revert a deletion by edge key without offsets.
Undo path for transaction rollback: scans every physical generation sharing the endpoint key, verifies this undo owns the deletion through the authority, then reverts both directions by edge id. Authority revival requires both directions to revert; a partial revert keeps the deletion mark and reports an error.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn staging_batch() -> EdgeStagingBatch
pub fn staging_batch() -> EdgeStagingBatch
Create an empty staging batch for one atomic group of edge writes.
Sourcepub fn commit_staging_batch(
&mut self,
batch: EdgeStagingBatch,
) -> Result<usize, StorageError>
pub fn commit_staging_batch( &mut self, batch: EdgeStagingBatch, ) -> Result<usize, StorageError>
Commit one staging batch atomically.
Entries apply in stage order with batch prefix effects: a later entry observes earlier entries of the same batch. An insert cancelled by a later delete of the same key leaves no tombstone and no visible edge; a delete followed by an insert of the same key rebuilds. Staged writes stay invisible to every read until commit. Prevalidation failures leave committed state untouched. When a late failure still occurs, only the entries this batch already applied are rolled back; committed data from other batches is never touched. Dropping a batch without committing discards it with no residue. Cancelled inserts consume at most the monotonic edge-id counter, never visible state.
Crash-atomic boundary: the batch is the atomic unit. Logical redo is
appended to the write-ahead log before any topology, timestamp or
property mutation, and replay is idempotent (inserts skip on
EdgeAlreadyExists, deletes treat missing edges as done), so a crash
at any point replays to either the whole batch visible or the whole
batch invisible. A torn apply can never leave single-direction
topology or ownerless timestamps behind: the load-time copy audit
counts such residue and rejects the load fail-closed. The manifest
publish order (group shards before metadata, manifest file last with
its tail embedded in meta.bin) is unchanged; only this contract and
its tests are new.
Returns the number of net applied entries (inserts plus deletes, excluding batch-cancelled pairs).
Sourcepub fn staging_group_plan(
&self,
batch: &EdgeStagingBatch,
) -> HashMap<u32, (usize, usize)>
pub fn staging_group_plan( &self, batch: &EdgeStagingBatch, ) -> HashMap<u32, (usize, usize)>
Per-owner commit split for one staging batch.
Pure routing helper: counts inserts and deletes per owner group so reservation, WAL grouping and future parallel applies stay group-local. No state change. The commit itself stays one serialized pass with prefix rollback, so any group failure still rolls back the whole batch and the caller observes one atomic commit.
Sourcepub fn write_contention_snapshot(&self) -> (usize, u64, u64, f64)
pub fn write_contention_snapshot(&self) -> (usize, u64, u64, f64)
Contention benchmark snapshot over committed write volume.
Returns (groups, total, max, skew) where skew is max over mean.
Decision gate for finer locking: partitioned lock-free applies or
vertex-level locks are only introduced when this snapshot proves a
bottleneck with measured data. The write path itself stays
table-serialized under the single-writer discipline.
Source§impl EdgeStore
impl EdgeStore
pub fn insert_edge( &mut self, src: u32, dst: u32, rank: i64, property_values: &[(Arc<str>, Value)], ts: u64, ) -> Result<(), StorageError>
Sourcepub fn insert_edges_batch(
&mut self,
entries: &[(u32, u32, i64, &[(Arc<str>, Value)], u64)],
) -> Result<(), StorageError>
pub fn insert_edges_batch( &mut self, entries: &[(u32, u32, i64, &[(Arc<str>, Value)], u64)], ) -> Result<(), StorageError>
Commit many inserts of one edge type in a single staging batch.
One prevalidation, one topology reservation pass and one live-index
rebuild for the whole batch instead of one per edge. Entries apply in
slice order with the same per-entry effects as repeated insert_edge
calls, including out/in symmetry and rollback of the applied prefix on
failure. An empty table takes the grouped direct-write path instead:
no rows, no timestamps and no frozen groups exist yet, so one log
append plus one reservation pass per direction produces exactly what
the per-entry staging commit would. Empty bundled tables qualify only
when every rank is pinned to zero (then each (src, dst) key is
unique and the value pass addresses one slot per leg); anything else
stays on the staging path so inline values ride the value column.
Sourcepub fn bulk_import_edges(
&mut self,
entries: &[(u32, u32, i64, &[(Arc<str>, Value)], u64)],
) -> Result<usize, StorageError>
pub fn bulk_import_edges( &mut self, entries: &[(u32, u32, i64, &[(Arc<str>, Value)], u64)], ) -> Result<usize, StorageError>
Partitioned direct-write bulk import for initial loads.
Empty-table fast path mirroring the partitioned direct-pack idea:
one prevalidation, one write-ahead append, one reservation pass per
direction, then one grouped topology write per direction preserving
per-edge timestamps, followed by batched authority, property, owner
and index writes. Topology uses the grouped bulk path (single
reservation and single live-set rebuild per row) instead of the
per-entry insert loop, so wide fanouts avoid per-edge index
maintenance during the topology phase. The commit point is unchanged:
groups stay dirty with their append sidecars and the next checkpoint
persists through the existing manifest protocol. Non-empty tables
must use insert_edges_batch. Bundled tables take the
bundled-specific import below instead of the columnar property
writes.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn freeze_feasibility(
&self,
outgoing: bool,
gid: usize,
) -> FreezeFeasibility
pub fn freeze_feasibility( &self, outgoing: bool, gid: usize, ) -> FreezeFeasibility
Read-only freeze outcome for one group of one direction.
outgoing selects the out or in shard set. Runs the same gate
freeze_group enforces without touching state.
Sourcepub fn freeze_group(
&mut self,
outgoing: bool,
gid: usize,
cutoff: u64,
reserve_ratio: f32,
) -> Result<u64, StorageError>
pub fn freeze_group( &mut self, outgoing: bool, gid: usize, cutoff: u64, reserve_ratio: f32, ) -> Result<u64, StorageError>
Freeze one group of one direction, returning its packed live count.
outgoing selects the out or in shard set. Reclaimable tombstones
below cutoff are dropped first and reported into the visibility
authority; a maximum cutoff packs verbatim. The frozen group rejects
writes until explicitly unfrozen.
Sourcepub fn unfreeze_group(
&mut self,
outgoing: bool,
gid: usize,
) -> Result<u64, StorageError>
pub fn unfreeze_group( &mut self, outgoing: bool, gid: usize, ) -> Result<u64, StorageError>
Unfreeze one group of one direction, returning its restored live count.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn migrate_record_form(
&mut self,
target: RecordForm,
) -> Result<MigrateStats, StorageError>
pub fn migrate_record_form( &mut self, target: RecordForm, ) -> Result<MigrateStats, StorageError>
Migrate this table to another record form offline.
Requires exclusive access and a checkpoint afterwards. Migrating to the current form is a no-op success.
Sourcepub fn switch_record_form_online(
&mut self,
target: RecordForm,
) -> Result<MigrateStats, StorageError>
pub fn switch_record_form_online( &mut self, target: RecordForm, ) -> Result<MigrateStats, StorageError>
Switch record forms in-table without closing the table.
Same pure rebuild as Self::migrate_record_form, run on the live
open table: &mut self already serializes every writer, so the switch
is atomic with respect to reads and writes without a close/reopen
window. Reads before the call observe the old form, reads after
observe the new form with identical logical content. A failed switch
leaves the original shards, authority and properties untouched. A
checkpoint afterwards is mandatory
(Self::is_migration_checkpoint_required); pre-switch WAL redo is
fenced at switch time and never replays onto the new form.
Sourcepub fn is_migration_checkpoint_required(&self) -> bool
pub fn is_migration_checkpoint_required(&self) -> bool
Whether a record-form switch completed since the last checkpoint.
Memory-only fence flag: while set, the table must be checkpointed before any WAL replay or further switch is trusted. Cleared by flush and load, which re-establish the checkpoint base.
Sourcepub fn migration_plan(
&self,
target: RecordForm,
) -> Result<MigrationPlan, StorageError>
pub fn migration_plan( &self, target: RecordForm, ) -> Result<MigrationPlan, StorageError>
Executable pre-switch checklist: prechecks, cost estimate and the mandatory-checkpoint requirement, computed without touching live state. A failing plan returns the same error the switch would, so callers can quote cost and abort before paying the rebuild.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn recommended_record_form(&self) -> RecordForm
pub fn recommended_record_form(&self) -> RecordForm
Recommend the record form for the current schema, strategies and observed profile.
Shape stays the veto: empty property sets on multi-edge legs stay pure, ineligible shapes stay columnar, and single-edge directions always resolve to columnar because only that form provides fixed single slots. Eligible single scalars then consult the profile: width over the slot limit or write-hot tables stay columnar, only narrow read-heavy tables recommend bundled. Unknown profiles stay columnar so old checkpoints without a snapshot never mis-trigger a migration.
Sourcepub fn auto_migrate_record_form_if_beneficial(
&mut self,
) -> Result<Option<MigrateStats>, StorageError>
pub fn auto_migrate_record_form_if_beneficial( &mut self, ) -> Result<Option<MigrateStats>, StorageError>
Switch to the recommended form when it differs from the current one.
Explicit opt-in entry: returns None when the table already holds the
recommended form, otherwise runs the same online switch as the manual
path (pure rebuild, WAL fence, mandatory checkpoint afterwards).
Guard failures (pending schema change, pending checkpoint, illegal
target for the live data) propagate as errors with the table
untouched. The caller must checkpoint after a successful switch.
Hysteresis: automatic migration only moves columnar to bundled; a
bundled table never auto-reverses on profile noise and needs a manual
migration back to columnar.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn reshard(
&mut self,
new_group_bits: u32,
) -> Result<ReshardStats, StorageError>
pub fn reshard( &mut self, new_group_bits: u32, ) -> Result<ReshardStats, StorageError>
Offline group-width change: the only adjustment outlet for the locked
address width. Reads the old width group by group and writes the new
width, then switches the manifest on the next checkpoint. There is no
online width-change branch: callers must hold exclusive access and
checkpoint after a successful reshard. A successful width change arms
the mandatory-checkpoint fence, so writes are rejected until the next
checkpoint persists the new width; the equal-width no-op arms nothing.
Vertex ids are unchanged, so the property index needs no rebuild.
Widths outside 1..=20 are rejected; the current width is a no-op success.
Operation guide: run offline with no other writer, flush with
flush_incremental after success, then reload to verify. The edge
logical set is unchanged and the audit must report no drift.
Selection guide: small tables tolerating downtime use offline reshard
for any width change; large tables tolerating no downtime keep the
current width and densify vertex ids offline first; small width
adjustments on idle tables prefer reshard, while online record-form
migration only changes the inline versus columnar layout and never
changes the address width.
Sourcepub fn remap_vertex_ids(
&mut self,
src_mapping: Option<&HashMap<u32, u32>>,
dst_mapping: Option<&HashMap<u32, u32>>,
) -> Result<(), StorageError>
pub fn remap_vertex_ids( &mut self, src_mapping: Option<&HashMap<u32, u32>>, dst_mapping: Option<&HashMap<u32, u32>>, ) -> Result<(), StorageError>
Propagate vertex compaction old-to-new internal ID mappings into this edge table.
Must run inside the compaction commit barrier held by the maintenance layer (vertex remap and edge rewrite share one barrier and one journal): the caller journaled the vertex mapping before this call, and an empty mapping short-circuits to a no-op so stable row IDs (free-stack reuse below the hole-rate watermark) produce zero edge writes.
The table references two vertex label ID spaces:
src_mappingapplies to out shard rows and in shard neighbor keysdst_mappingapplies to in shard rows and out shard neighbor keys
Both directions are rebuilt group by group with trailing empty groups dropped. The property index encodes (src, dst) internal IDs in its keys and is rebuilt from the remapped data when enabled. Unmapped endpoints keep their original value; misses are debug logged only.
Sourcepub fn remap_vertex_ids_with_stats(
&mut self,
src_mapping: Option<&HashMap<u32, u32>>,
dst_mapping: Option<&HashMap<u32, u32>>,
) -> Result<RemapStats, StorageError>
pub fn remap_vertex_ids_with_stats( &mut self, src_mapping: Option<&HashMap<u32, u32>>, dst_mapping: Option<&HashMap<u32, u32>>, ) -> Result<RemapStats, StorageError>
Same as remap_vertex_ids but returns miss counters for tests.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn pending_add_column(&self) -> Option<&PendingAddColumn>
pub fn pending_add_column(&self) -> Option<&PendingAddColumn>
Borrow the pending add-column change, if any.
Sourcepub fn prepare_add_property(
&mut self,
name: String,
data_type: DataType,
nullable: bool,
default_value: Option<Value>,
) -> Result<(), StorageError>
pub fn prepare_add_property( &mut self, name: String, data_type: DataType, nullable: bool, default_value: Option<Value>, ) -> Result<(), StorageError>
Validate an add-column request without touching storage or schema.
Sourcepub fn fill_pending_add_property(&mut self) -> Result<(), StorageError>
pub fn fill_pending_add_property(&mut self) -> Result<(), StorageError>
Build the physical column for the prepared change.
Uses the single column-construction implementation shared with the single-step path.
Sourcepub fn publish_pending_add_property(&mut self) -> Result<(), StorageError>
pub fn publish_pending_add_property(&mut self) -> Result<(), StorageError>
Activate the filled change: schema entry, name cache, history record.
Publishing is the durability boundary; the pending slot is cleared so a later crash replays the published schema only.
Sourcepub fn abort_pending_add_property(&mut self) -> Result<(), StorageError>
pub fn abort_pending_add_property(&mut self) -> Result<(), StorageError>
Drop the pending change, removing the physical column when filled.
Never touches published schema or history: prepared changes built nothing, filled changes only built the unpublished physical column.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn pending_drop_column(&self) -> Option<&PendingDropColumn>
pub fn pending_drop_column(&self) -> Option<&PendingDropColumn>
Borrow the pending drop-column change, if any.
Sourcepub fn prepare_drop_property(&mut self, name: &str) -> Result<(), StorageError>
pub fn prepare_drop_property(&mut self, name: &str) -> Result<(), StorageError>
Validate a drop-column request and snapshot the doomed column.
Touches nothing: the schema, the physical column and history stay unchanged until publishing.
Sourcepub fn publish_pending_drop_property(&mut self) -> Result<(), StorageError>
pub fn publish_pending_drop_property(&mut self) -> Result<(), StorageError>
Execute the prepared drop: physical column, schema entry, history.
A history failure restores the snapshotted column, schema entry and name cache, leaving the table as if publishing never ran. The pending slot is kept on failure so the caller can retry or abort.
Sourcepub fn abort_pending_drop_property(&mut self) -> Result<(), StorageError>
pub fn abort_pending_drop_property(&mut self) -> Result<(), StorageError>
Drop the pending change. Nothing was removed yet, so there is nothing to restore: prepared drops built only the in-memory snapshot.
Source§impl EdgeStore
impl EdgeStore
Sourcepub fn pending_rename_column(&self) -> Option<&PendingRenameColumn>
pub fn pending_rename_column(&self) -> Option<&PendingRenameColumn>
Borrow the pending rename-column change, if any.
Sourcepub fn prepare_rename_property(
&mut self,
old_name: &str,
new_name: &str,
) -> Result<(), StorageError>
pub fn prepare_rename_property( &mut self, old_name: &str, new_name: &str, ) -> Result<(), StorageError>
Validate a rename-column request and snapshot the affected names.
Touches nothing: the physical column, the schema, the name cache and history stay unchanged until publishing. Renaming preserves column arity, so inline record forms need no rebuild hint here, unlike add/drop which change the property count.
Sourcepub fn publish_pending_rename_property(&mut self) -> Result<(), StorageError>
pub fn publish_pending_rename_property(&mut self) -> Result<(), StorageError>
Execute the prepared rename: log, physical column, schema entry, name cache, history.
The log entry is appended before any mutation so a crash after the log but before the checkpoint replays the rename. A history failure renames everything back, leaving the table as if publishing never ran. The pending slot is kept on failure so the caller can retry or abort.
Sourcepub fn abort_pending_rename_property(&mut self) -> Result<(), StorageError>
pub fn abort_pending_rename_property(&mut self) -> Result<(), StorageError>
Drop the pending change. Nothing was renamed yet, so there is nothing to restore.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for EdgeStore
impl !RefUnwindSafe for EdgeStore
impl !UnwindSafe for EdgeStore
impl Send for EdgeStore
impl Sync for EdgeStore
impl Unpin for EdgeStore
impl UnsafeUnpin for EdgeStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.