pub struct MutableCsr { /* private fields */ }Expand description
Mutable multi-edge CSR with lazy primary blocks and overflow chains.
Single-writer discipline: this type carries no internal locks. Concurrent
readers are safe while no mutation is in flight; concurrent writers must
be serialized by the caller (table or transaction layer). Vertex-level
locking is a caller decision, not a property of this struct. Multi-core
work is carried by shard-level parallelism over NodeGroup shards
(freeze, reclaim, read-only scans); point-write parallelism is an import
pressure decision above this layer, and no row lock is added here before
that measurement.
Implementations§
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn compact_with_ts_reporting(
&mut self,
cutoff: u64,
reserve_ratio: f32,
on_edge_removed: &mut dyn FnMut(EdgeId, u64),
) -> usize
pub fn compact_with_ts_reporting( &mut self, cutoff: u64, reserve_ratio: f32, on_edge_removed: &mut dyn FnMut(EdgeId, u64), ) -> usize
Compact with per-edge removal reporting (on_edge_removed receives
each dropped edge id and delete timestamp).
Two read-only passes over the old layout, one allocation for the new primary list: the first pass counts kept entries per row and sizes capacities, the second copies kept entries straight into place. No intermediate staging copy of every kept edge is built, so the transient peak is one new list instead of a staging copy plus a new list. Overflow chains are eliminated: every kept entry lands in its row’s primary block.
Sourcepub fn reclaimable_count(&self, vid: u32, cutoff: u64) -> usize
pub fn reclaimable_count(&self, vid: u32, cutoff: u64) -> usize
Count entries of one vertex reclaimable at cutoff.
Only tombstones eligible under the shared collection predicate count; tombstones pinned by older snapshots are left alone.
Sourcepub fn vertex_needs_compact(&self, vid: u32, cutoff: u64) -> bool
pub fn vertex_needs_compact(&self, vid: u32, cutoff: u64) -> bool
Whether one vertex holds anything reclaimable at cutoff.
Sourcepub fn vertex_reclaim_probe(&self, vid: u32, cutoff: u64) -> (usize, usize)
pub fn vertex_reclaim_probe(&self, vid: u32, cutoff: u64) -> (usize, usize)
Single-walk reclaim probe of one vertex: (dead, reclaimable).
Fuses the census and eligibility walks so maintenance passes pay one
row scan instead of two. dead counts every tombstoned entry
regardless of eligibility; reclaimable counts only entries the
current cutoff already covers.
Sourcepub fn vertex_census(&self, vid: u32) -> (usize, usize, usize)
pub fn vertex_census(&self, vid: u32) -> (usize, usize, usize)
Physical entry census of one vertex: (live, dead, capacity).
live counts entries with no deletion stamp, dead counts
tombstoned entries regardless of eligibility, and capacity is the
reserved row capacity plus allocated overflow chunks.
Sourcepub fn compact_vertex_with_reporting(
&mut self,
vid: u32,
cutoff: u64,
on_edge_removed: &mut dyn FnMut(EdgeId, u64),
) -> usize
pub fn compact_vertex_with_reporting( &mut self, vid: u32, cutoff: u64, on_edge_removed: &mut dyn FnMut(EdgeId, u64), ) -> usize
Reclaim one vertex in place, leaving every other row untouched.
Eligible tombstones are dropped and reported through
on_edge_removed; live entries and pinned tombstones are tightened
to the front of their current slots. Row offsets and capacities of
other vertices never move, so the work stays proportional to the
degree of vid instead of the size of the table.
Source§impl MutableCsr
impl MutableCsr
pub fn new() -> MutableCsr
pub fn with_capacity(vertex_capacity: usize, edge_capacity: usize) -> MutableCsr
pub fn with_overflow_chunk_edges( vertex_capacity: usize, edge_capacity: usize, overflow_chunk_edges: usize, ) -> MutableCsr
Sourcepub fn set_tombstone_reuse_cutoff(&mut self, cutoff: u64)
pub fn set_tombstone_reuse_cutoff(&mut self, cutoff: u64)
Watermark-derived cutoff gating hot-path tombstone reuse. The cutoff carries exactly one freshness contract, shared by every caller:
- Only a global-watermark capture (
MvccWatermarks::captureplus margin) may refresh it. The table-local pin cache is never a source: it can sit above the global safe point and would widen reuse past what reclaim has proven. - The sentinel
Timestamp::MAXdisables reuse. A maintenance pass that cannot capture a fresh watermark must clear instead of reusing, so an unrefreshed table degrades to the pre-reuse behavior. - This setter only narrows reuse: the stored cutoff moves to the
minimum of the old and new values, so a stale larger value can never
widen reuse. Fresh watermark advances use
refresh_from_watermark.
Sourcepub fn refresh_tombstone_reuse_cutoff(&mut self, fresh: u64)
pub fn refresh_tombstone_reuse_cutoff(&mut self, fresh: u64)
Fresh watermark refresh of the reuse cutoff, allowing widening.
Only call with a freshly captured global watermark bound. Stale values
must go through set_tombstone_reuse_cutoff so they can only narrow.
Sourcepub fn clear_tombstone_reuse_cutoff(&mut self)
pub fn clear_tombstone_reuse_cutoff(&mut self)
Drop the reuse hint back to the disabled sentinel.
Stale path of the freshness contract above: when no fresh watermark is available, reuse stops entirely instead of running on an expired bound.
Sourcepub fn tombstone_reuse_cutoff(&self) -> u64
pub fn tombstone_reuse_cutoff(&self) -> u64
Current reuse cutoff for observability and tests. Timestamp::MAX
means reuse is disabled.
pub fn vertex_capacity(&self) -> usize
pub fn edge_count(&self) -> u64
Sourcepub fn resize(&mut self, new_vertex_capacity: usize)
pub fn resize(&mut self, new_vertex_capacity: usize)
Resize vertex capacity (requires exclusive access)
Sourcepub fn ensure_vertex_capacity(&mut self, min_capacity: usize)
pub fn ensure_vertex_capacity(&mut self, min_capacity: usize)
Ensure vertex capacity (grows if needed)
Sourcepub fn get_overflow_chunks(&self, vid: u32) -> Option<&Vec<OverflowChunk>>
pub fn get_overflow_chunks(&self, vid: u32) -> Option<&Vec<OverflowChunk>>
Get overflow chunks for a vertex.
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn iter(&self, ts: u64) -> MutableCsrIterator<'_> ⓘ
pub fn iter(&self, ts: u64) -> MutableCsrIterator<'_> ⓘ
Create iterator over all edges
Sourcepub fn iter_all(&self) -> MutableCsrIterator<'_> ⓘ
pub fn iter_all(&self) -> MutableCsrIterator<'_> ⓘ
Create an iterator over all physically present edges, including entries marked as deleted (delete_ts != MAX). Used when rebuilding the CSR so tombstoned entries survive remapping.
Sourcepub fn iter_edges_of(&self, src_vid: u32, ts: u64) -> VertexEdgesIter<'_> ⓘ
pub fn iter_edges_of(&self, src_vid: u32, ts: u64) -> VertexEdgesIter<'_> ⓘ
Iterate edges of a vertex without collecting into a Vec. Test-only row-stamp filtered iterator; production scans go through the version authority. Filters by the delete replica only: creation stamps live in the table authority, not in the row, so this probe can never decide full MVCC visibility on its own.
Source§impl MutableCsr
impl MutableCsr
pub fn has_live_set(&self, vid: &u32) -> bool
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn dump(&self) -> Vec<u8> ⓘ
pub fn dump(&self) -> Vec<u8> ⓘ
Dump to bytes, current encoded version.
Header columns (offsets, degrees, capacities) and primary neighbor columns (endpoints, ranks, edge ids, stamps) persist through the integer column path with per-column bit-packing or run-length encoding and a narrow plain fallback. Overflow chunks use the same column path per chunk instead of plain neighbor records.
Format:
- format marker (u32 = encoded or raw mode, see serialization)
- vertex_capacity (u64)
- edge_count (u64)
- primary_len (u64)
- overflow_chunk_edges (u64)
- tombstone_reuse_cutoff (u64, MAX disables reuse)
- encoded offsets column
- encoded degrees column
- encoded capacities column
- encoded endpoints column
- encoded ranks column
- encoded edge ids column
- encoded create stamps column
- encoded delete stamps column
- per-vertex overflow chunks (column-encoded per chunk)
- payload CRC32 (u32, little-endian, over all preceding bytes)
Sourcepub fn dump_into(&self, out: &mut Vec<u8>)
pub fn dump_into(&self, out: &mut Vec<u8>)
Borrow-based dump into out without an intermediate owned buffer.
Byte-identical to dump; checkpoint writes use this so no whole-group
clone or temporary dump buffer is needed.
Sourcepub fn dump_into_with_scratch(
&self,
out: &mut Vec<u8>,
scratch: &mut CsrDumpScratch,
)
pub fn dump_into_with_scratch( &self, out: &mut Vec<u8>, scratch: &mut CsrDumpScratch, )
Dump reusing caller-owned column buffers.
Same bytes as Self::dump_into but the four neighbor-column
buffers are cleared and refilled instead of reallocated, so a
checkpoint over many groups pays one allocation per column instead
of one per group. The scratch holds no state between calls.
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.
Measures the winning encoding per column without changing in-memory state. Neighbor and edge-id columns are reported first, offset and length columns follow; all use the integer column path only.
Sourcepub fn load(&mut self, data: &[u8]) -> Result<(), StorageError>
pub fn load(&mut self, data: &[u8]) -> Result<(), StorageError>
Load from bytes. Only the single format marker is accepted; any other marker is corrupt. Strict validation below is shared by every payload that passes the marker gate. The trailing CRC32 is verified before any parsing so a truncated or bit-rotted checkpoint fails fast instead of decoding garbage.
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn is_primary_sorted(&self, src_vid: u32) -> bool
pub fn is_primary_sorted(&self, src_vid: u32) -> bool
Whether the primary block of one row arrives in key order.
Ground-truth order probe over the primary window: threshold scans consult the cached per-row flag instead of paying this walk on every query. After a maintenance sort the primary block is ordered while overflow chunks stay in insertion order as the unsorted suffix.
Sourcepub fn is_row_sorted(&self, src_vid: u32) -> bool
pub fn is_row_sorted(&self, src_vid: u32) -> bool
Whether the live entries of one row arrive in key order.
Mutable rows are insertion-ordered, so this usually reports false on
multi-edge rows and true on empty or single-entry rows. Only frozen,
mapped and single-slot rows promise order and may use bisection;
this observation must not be cached across restarts (the underlying
flag is memory-only and rebuilt on load). Rows with an unsorted
overflow suffix report false even when the primary prefix is sorted;
threshold scans still bisect the prefix via is_primary_sorted.
Sourcepub fn nbr_at_offset(&self, src_vid: u32, offset: i32) -> Option<Nbr>
pub fn nbr_at_offset(&self, src_vid: u32, offset: i32) -> Option<Nbr>
Read-only view of one primary slot without mutating state.
Used to verify that a caller-supplied offset still addresses the expected edge before a destructive offset write runs.
Sourcepub fn get_edge_physical(&self, src_vid: u32, dst: VertexId) -> Option<Nbr>
pub fn get_edge_physical(&self, src_vid: u32, dst: VertexId) -> Option<Nbr>
Locate the first live edge by endpoint without consulting snapshots.
Physical addressing only; tombstoned and gap slots are skipped. Snapshot visibility is decided by the version authority above this layer, while tombstone access uses the full physical views.
Wide rows answer from the endpoint location index: a present key addresses its slot directly, an absent key returns without scanning. Narrow rows without an index fall through to the linear walk.
Sourcepub fn physical_edges_of(&self, src_vid: u32) -> Vec<Nbr>
pub fn physical_edges_of(&self, src_vid: u32) -> Vec<Nbr>
Every physically stored entry of one vertex without timestamp filtering.
Visibility is decided by the version authority above this layer.
Test and offline use; production scans use Self::fill_physical_into
or the visitor paths instead of this allocating accessor.
Sourcepub fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>)
pub fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>)
Fill a caller buffer with every physically stored entry of one vertex.
Same content as the allocating accessor above, without the per-vertex allocation. Batch scans reuse one buffer across vertices and slice it per vertex instead of collecting one vector per vertex. Gap sentinels are excluded; tombstones are included for reclaim and audit paths.
Sourcepub fn fill_physical_into_batched(
&self,
src_vids: &[u32],
out: &mut Vec<Nbr>,
starts: &mut Vec<usize>,
)
pub fn fill_physical_into_batched( &self, src_vids: &[u32], out: &mut Vec<Nbr>, starts: &mut Vec<usize>, )
Fill a caller buffer with the physical entries of many vertices.
Batched counterpart of Self::fill_physical_into for fan-out scans:
one clear plus one reservation serves every listed vertex, and starts
records the boundary of each vertex so the caller slices per vertex
without a second lookup. Gap sentinels are excluded; tombstones are
included, matching the single-vertex walk exactly.
Sourcepub fn visit_hot_batched<F>(&self, src_vids: &[u32], f: F)
pub fn visit_hot_batched<F>(&self, src_vids: &[u32], f: F)
Visit every physically stored hot half of many vertices without allocating and without touching the stamp lines.
Batched counterpart of Self::visit_hot for fan-out traversals that
resolve visibility through the version authority by edge_id.
Sourcepub fn visit_hot<F>(&self, src_vid: u32, f: F)
pub fn visit_hot<F>(&self, src_vid: u32, f: F)
Visit every physically stored hot half of one vertex without allocating and without touching the stamp lines.
Hot-only counterpart of Self::visit_physical for traversals that
resolve visibility through the version authority by edge_id. Stamps
stay out of cache on this walk. Consolidated rows read their single
overflow block without the chain loop. Gap sentinels are skipped;
tombstones are visited.
Sourcepub fn visit_physical<F>(&self, src_vid: u32, f: F)
pub fn visit_physical<F>(&self, src_vid: u32, f: F)
Visit every physically stored entry of one vertex without allocating.
The visitor returns false to stop early. Visibility is decided by the version authority above this layer. Consolidated rows read their single overflow block without the chain loop. Gap sentinels are skipped; tombstones are visited.
Sourcepub fn visit_physical_with_position<F>(&self, src_vid: u32, f: F)
pub fn visit_physical_with_position<F>(&self, src_vid: u32, f: F)
Visit every physically stored entry with its row position.
Same walk as Self::visit_physical but the visitor also receives
the EdgePosition of each entry, so a later delete or revert can
address the slot directly instead of rescanning the row. Positions
stay valid only until the next compaction, rebalance, repack or
removal of this row; positional writes revalidate the edge id. Gap
sentinels are skipped; tombstones are visited.
Sourcepub fn locate_edge(
&self,
src_vid: u32,
edge_id: EdgeId,
) -> Option<(EdgePosition, Nbr)>
pub fn locate_edge( &self, src_vid: u32, edge_id: EdgeId, ) -> Option<(EdgePosition, Nbr)>
Locate the first entry with edge_id, returning its position and copy.
Single scan shared by delete and revert callers that then act on the position directly.
Sourcepub fn primary_contains(&self, src_vid: u32, edge_id: EdgeId) -> bool
pub fn primary_contains(&self, src_vid: u32, edge_id: EdgeId) -> bool
Whether the primary row of one vertex holds edge_id.
Used to distinguish a stale offset (edge lives in primary at a different offset, must fail) from an overflow row (no offset can address it, may fall back to the edge-id path).
Sourcepub fn has_physical_entries(&self, vid: u32) -> bool
pub fn has_physical_entries(&self, vid: u32) -> bool
Whether one vertex holds any physically stored entry.
Sourcepub fn edges_of(&self, src_vid: u32, ts: u64) -> Vec<Nbr>
pub fn edges_of(&self, src_vid: u32, ts: u64) -> Vec<Nbr>
Get edges of a vertex at a given timestamp.
UNFILTERED single-predicate read for tests and offline tools only: filters by row delete stamp without consulting the version authority (no creation-stamp or authority verdict). Production traversals must use the visitor or caller-buffer fill paths with the authority gate.
Sourcepub fn get_edge(&self, src_vid: u32, dst: VertexId, ts: u64) -> Option<Nbr>
pub fn get_edge(&self, src_vid: u32, dst: VertexId, ts: u64) -> Option<Nbr>
Get a specific edge
UNFILTERED single-predicate read for tests and offline tools only.
Wide rows consult the endpoint location index first: a present key
addresses its slot directly when the live entry covers ts. Absent
keys at the maximum timestamp return without scanning; any other
timestamp falls through to the row walk so historically visible
tombstoned versions are still found.
Sourcepub fn visit_threshold<F>(
&self,
src_vid: u32,
lower: Option<(u32, i64)>,
upper: Option<(u32, i64)>,
f: F,
)
pub fn visit_threshold<F>( &self, src_vid: u32, lower: Option<(u32, i64)>, upper: Option<(u32, i64)>, f: F, )
Visit live entries whose (endpoint, rank) key falls in the inclusive
[lower, upper] range (None means unbounded).
Sorted primary prefixes bisect to the key window and filter for
liveness inside it, then the overflow suffix always scans linearly.
Unsorted primaries scan linearly with the same key predicate. The
prefix probe is the cached per-row order flag, so hybrid rows (sorted
primary plus unsorted overflow) still bisect the prefix without
paying a full sortedness walk on every query. Only live entries
(delete_ts == MAX) are visited; snapshot visibility stays above.
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn row_gap(&self, vid: u32) -> usize
pub fn row_gap(&self, vid: u32) -> usize
Reserved primary slots minus live primary entries of one row.
Sourcepub fn row_density(&self, vid: u32) -> f32
pub fn row_density(&self, vid: u32) -> f32
Live primary entries per unit of reserved primary capacity.
Sourcepub fn rebalance_row(&mut self, vid: u32) -> bool
pub fn rebalance_row(&mut self, vid: u32) -> bool
Rebalance one row in place: tighten live primary entries to the front, pull overflow live entries into primary gaps, and repack any leftover overflow into graded chunks. Primary capacity never grows here, so other rows never move; capacity growth stays with full and region compactions that size rows at the density target. Pinned tombstones stay in place. Returns true when overflow shrank.
Sourcepub fn sort_row(&mut self, vid: u32) -> bool
pub fn sort_row(&mut self, vid: u32) -> bool
Sort one primary row into (endpoint, rank, edge_id) order.
Maintenance-only entry: sorting moves slots, so every previously
issued EdgePosition for this row becomes stale and the caller must
relocate through the edge-id key first. The reuse hint is dropped,
the cached primary order is established, and the live index is
rebuilt into its sorted form. Overflow chunks stay in insertion
order and remain the unsorted suffix. New writes keep the hot path
(primary gap fill, then overflow tail) and therefore clear the cached
order again; the next maintenance pass re-sorts. The flag is never
persisted: load resets every row to unordered and the maintenance
pass re-establishes order.
Sourcepub fn sort_all_rows(&mut self) -> usize
pub fn sort_all_rows(&mut self) -> usize
Sort every primary row that is out of order. Returns reordered rows.
Same maintenance-only invalidation as sort_row, applied row by row.
Source§impl MutableCsr
impl MutableCsr
pub fn overflow_chunk_allocs(&self) -> u64
pub fn primary_block_allocs(&self) -> u64
pub fn repack_count(&self) -> u64
pub fn tombstone_reuse_count(&self) -> u64
pub fn tombstone_reuse_hint_hits(&self) -> u64
pub fn tombstone_reuse_hint_misses(&self) -> u64
pub fn tombstone_reuse_hint_hit_rate(&self) -> f32
pub fn live_set_rebuild_count(&self) -> u64
pub fn reset_baseline_counters(&mut self)
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn used_memory_size(&self) -> usize
pub fn used_memory_size(&self) -> usize
Get used memory size, counting reserved topology plus indexes.
Covers the primary neighbor list, the offset/degree/capacity arrays, the per-row order flags, reserved overflow chunk buffers, the dedup live sets and this struct. Tombstone authority memory is accounted by the table layer through the shared tombstone estimate so both stay on one caliber.
Sourcepub fn fragmentation_ratio(&self) -> f32
pub fn fragmentation_ratio(&self) -> f32
Compute fragmentation ratio as wasted share of reserved capacity.
Single caliber shared with FragmentationStats: wasted / total,
ranging from 0.0 (perfect packing) to below 1.0. A ratio above 0.5
marks a waste-dominated table worth a group merge; the write path
triggers on per-vertex reclaimable counts instead of this ratio.
Sourcepub fn get_fragmentation_stats(&self) -> FragmentationStats
pub fn get_fragmentation_stats(&self) -> FragmentationStats
Get detailed fragmentation statistics.
Both counters derive from the live structures: dead entries are the physically stored entries minus live edges (primary tombstones plus overflow dead entries), and wasted capacity is the reserved capacity minus live edges (row gaps plus tombstone slots).
Source§impl MutableCsr
impl MutableCsr
Sourcepub fn insert_edge(
&mut self,
src_vid: u32,
dst: VertexId,
edge_id: EdgeId,
ts: u64,
) -> Result<(), StorageError>
pub fn insert_edge( &mut self, src_vid: u32, dst: VertexId, edge_id: EdgeId, ts: u64, ) -> Result<(), StorageError>
Insert an edge with automatic capacity expansion
Sourcepub fn delete_edge(
&mut self,
src_vid: u32,
edge_id: EdgeId,
ts: u64,
) -> Result<bool, StorageError>
pub fn delete_edge( &mut self, src_vid: u32, edge_id: EdgeId, ts: u64, ) -> Result<bool, StorageError>
Delete an edge by edge_id.
Returns Ok(true) when deleted, Ok(false) when the edge does not
exist or is not deletable at ts, and
Err(StorageError::write_write_conflict) when the edge was already
deleted at a different timestamp (write-write conflict at the storage
layer, surfaced immediately instead of a silent false).
Sourcepub fn delete_edge_by_dst_reporting(
&mut self,
src_vid: u32,
dst: VertexId,
ts: u64,
on_deleted: &mut dyn FnMut(EdgeId),
) -> usize
pub fn delete_edge_by_dst_reporting( &mut self, src_vid: u32, dst: VertexId, ts: u64, on_deleted: &mut dyn FnMut(EdgeId), ) -> usize
Delete edges by destination vertex with full-match semantics.
Deletes every live match and returns the deleted count so table
rollback can reconcile by count. One call deletes the whole match.
Single pass: primary and overflow rows are stamped in place while
walking, so no match vector is materialized and no second scan runs.
Every stamped id is reported through on_deleted for append-log and
audit callers that previously paid a separate collection scan.
Sourcepub fn delete_edge_by_dst_reporting_positioned(
&mut self,
src_vid: u32,
dst: VertexId,
ts: u64,
on_deleted: &mut dyn FnMut(EdgeId, EdgePosition),
) -> usize
pub fn delete_edge_by_dst_reporting_positioned( &mut self, src_vid: u32, dst: VertexId, ts: u64, on_deleted: &mut dyn FnMut(EdgeId, EdgePosition), ) -> usize
Position-reporting form of Self::delete_edge_by_dst_reporting.
Same single stamping pass, but each stamped id arrives with its row position so the caller can revert or re-delete the exact slot without rescanning. Positions are row-local and expire on the next compaction, rebalance, repack or removal of the row.
Sourcepub fn delete_edge_by_dst(
&mut self,
src_vid: u32,
dst: VertexId,
ts: u64,
) -> usize
pub fn delete_edge_by_dst( &mut self, src_vid: u32, dst: VertexId, ts: u64, ) -> usize
Delete edges by destination vertex with full-match semantics.
Deletes every live match and returns the deleted count so table rollback can reconcile by count. One call deletes the whole match.
pub fn delete_edge_by_offset( &mut self, src_vid: u32, offset: i32, ts: u64, ) -> Result<bool, StorageError>
Sourcepub fn revert_delete_by_offset(
&mut self,
src_vid: u32,
offset: i32,
ts: u64,
) -> bool
pub fn revert_delete_by_offset( &mut self, src_vid: u32, offset: i32, ts: u64, ) -> bool
Revert a deleted edge by offset position in the primary block.
Only reverts deletions that occurred at or before the given timestamp. This maintains MVCC semantics during transaction rollback: we can only undo deletions that happened before the rollback point.
Sourcepub fn rollback_insert(&mut self, src_vid: u32, edge_id: EdgeId) -> bool
pub fn rollback_insert(&mut self, src_vid: u32, edge_id: EdgeId) -> bool
Erase one just-inserted edge for insert rollback.
Rollback-only path: erases the slot and updates degree/edge count,
leaving no tombstone trace, so a failed double-write can pretend the
edge never existed. This is deliberately distinct from MVCC deletes
(delete_edge family), which stamp delete_ts and stay visible to
the reclaim machinery (reclaimable_count, vertex_reclaim_probe,
fragmentation_ratio). Physical erasures free their slots inline
and therefore never appear in tombstone statistics; mixing the two
models on one row is expected (erasure for insert rollback,
tombstones for MVCC) and both keep the capacity ledger and the live
index exact.
Sourcepub fn revert_delete_by_edge_id(
&mut self,
src_vid: u32,
edge_id: EdgeId,
ts: u64,
) -> bool
pub fn revert_delete_by_edge_id( &mut self, src_vid: u32, edge_id: EdgeId, ts: u64, ) -> bool
Revert a deletion of an edge by edge id.
Restores delete_ts to MAX when the entry was deleted at or before the
given timestamp. Used to roll back the out-direction when the
in-direction deletion fails.
Sourcepub fn reserve_for_batch(&mut self, counts: &[(u32, usize)])
pub fn reserve_for_batch(&mut self, counts: &[(u32, usize)])
Reserve primary capacity for a batch of (src, count) pairs.
Each touched row is sized once at the packed density target for its current live width plus the incoming count, so the following inserts land in reserved gaps instead of allocating chunk by chunk. Rows not listed are untouched. Live sets are left alone; per-edge inserts keep maintaining them incrementally.
Two growth paths share the sizing above. Small batches touching few rows append fresh blocks at the tail, moving only touched rows and leaving every other offset stable; the superseded blocks stay in the vectors as orphaned filler until the next compaction reclaims them. Large batches touching a substantial share of rows use the single front-to-back rebuild, which moves each reserved byte exactly once and reclaims all orphaned filler. Row positions are row-relative, so neither path disturbs the live-key index.
Sourcepub fn batch_put_edges(
&mut self,
groups: &[(u32, Vec<(u32, i64, EdgeId, u64)>)],
check_duplicates: bool,
) -> Result<usize, StorageError>
pub fn batch_put_edges( &mut self, groups: &[(u32, Vec<(u32, i64, EdgeId, u64)>)], check_duplicates: bool, ) -> Result<usize, StorageError>
Bulk insert pre-grouped edges: groups holds one (src, batch) per
touched row with decoded (endpoint, rank, edge_id, ts) tuples.
Each row is reserved once at the packed density target, written in a
single pass, and has its live set rebuilt once instead of per edge.
Duplicate keys inside or against the row are rejected before any write
when check_duplicates is set; otherwise the caller guarantees
uniqueness. Returns the inserted edge count.
Sourcepub fn delete_edge_at_position(
&mut self,
src_vid: u32,
position: EdgePosition,
expected: EdgeId,
ts: u64,
) -> Result<bool, StorageError>
pub fn delete_edge_at_position( &mut self, src_vid: u32, position: EdgePosition, expected: EdgeId, ts: u64, ) -> Result<bool, StorageError>
Delete the edge at position when it still holds expected id.
Stale positions (row moved since the read) are refused with Ok(false)
instead of touching the wrong edge. Timestamp conflicts follow the
same state machine as the edge-id path.
Sourcepub fn revert_delete_at_position(
&mut self,
src_vid: u32,
position: EdgePosition,
expected: EdgeId,
ts: u64,
) -> bool
pub fn revert_delete_at_position( &mut self, src_vid: u32, position: EdgePosition, expected: EdgeId, ts: u64, ) -> bool
Revert the deletion at position when it still holds expected id
and the tombstone predates ts. Stale positions are refused with
false.
Trait Implementations§
Source§impl Clone for MutableCsr
impl Clone for MutableCsr
Source§fn clone(&self) -> MutableCsr
fn clone(&self) -> MutableCsr
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl CsrBase for MutableCsr
impl CsrBase for MutableCsr
Source§fn vertex_capacity(&self) -> usize
fn vertex_capacity(&self) -> usize
Send + Sync bounds express single-writer transfer across threads,
not lock-free concurrent writes. Multi-core scaling comes from
shard-level parallelism over NodeGroup shards above this trait;
the CSR lock model itself stays single-writer with no row locks.fn edge_count(&self) -> u64
fn dump(&self) -> Vec<u8> ⓘ
Source§fn dump_into(&self, out: &mut Vec<u8>)
fn dump_into(&self, out: &mut Vec<u8>)
dump into out without an intermediate
owned buffer, so checkpoint writes borrow the live topology instead
of cloning it for serialization.fn load(&mut self, data: &[u8]) -> Result<(), StorageError>
Source§impl Debug for MutableCsr
impl Debug for MutableCsr
Source§impl Default for MutableCsr
impl Default for MutableCsr
Source§fn default() -> MutableCsr
fn default() -> MutableCsr
Source§impl MutableCsrTrait for MutableCsr
impl MutableCsrTrait for MutableCsr
Source§fn insert_edge(
&mut self,
src_vid: u32,
dst: VertexId,
edge_id: EdgeId,
ts: u64,
) -> Result<(), StorageError>
fn insert_edge( &mut self, src_vid: u32, dst: VertexId, edge_id: EdgeId, ts: u64, ) -> Result<(), StorageError>
Source§fn delete_edge(
&mut self,
src_vid: u32,
edge_id: EdgeId,
ts: u64,
) -> Result<bool, StorageError>
fn delete_edge( &mut self, src_vid: u32, edge_id: EdgeId, ts: u64, ) -> Result<bool, StorageError>
Source§fn delete_edge_by_dst(&mut self, src_vid: u32, dst: VertexId, ts: u64) -> usize
fn delete_edge_by_dst(&mut self, src_vid: u32, dst: VertexId, ts: u64) -> usize
Source§fn delete_edge_by_dst_reporting(
&mut self,
src_vid: u32,
dst: VertexId,
ts: u64,
on_deleted: &mut dyn FnMut(EdgeId),
) -> usize
fn delete_edge_by_dst_reporting( &mut self, src_vid: u32, dst: VertexId, ts: u64, on_deleted: &mut dyn FnMut(EdgeId), ) -> usize
Source§fn delete_edge_by_dst_reporting_positioned(
&mut self,
src_vid: u32,
dst: VertexId,
ts: u64,
on_deleted: &mut dyn FnMut(EdgeId, Option<EdgePosition>),
) -> usize
fn delete_edge_by_dst_reporting_positioned( &mut self, src_vid: u32, dst: VertexId, ts: u64, on_deleted: &mut dyn FnMut(EdgeId, Option<EdgePosition>), ) -> usize
Source§fn locate_edge(
&self,
src_vid: u32,
edge_id: EdgeId,
) -> Option<(EdgePosition, Nbr)>
fn locate_edge( &self, src_vid: u32, edge_id: EdgeId, ) -> Option<(EdgePosition, Nbr)>
Source§fn delete_edge_at_position(
&mut self,
src_vid: u32,
position: EdgePosition,
expected: EdgeId,
ts: u64,
) -> Result<bool, StorageError>
fn delete_edge_at_position( &mut self, src_vid: u32, position: EdgePosition, expected: EdgeId, ts: u64, ) -> Result<bool, StorageError>
Source§fn revert_delete_at_position(
&mut self,
src_vid: u32,
position: EdgePosition,
expected: EdgeId,
ts: u64,
) -> bool
fn revert_delete_at_position( &mut self, src_vid: u32, position: EdgePosition, expected: EdgeId, ts: u64, ) -> bool
Source§fn delete_edge_by_offset(
&mut self,
src_vid: u32,
offset: i32,
ts: u64,
) -> Result<bool, StorageError>
fn delete_edge_by_offset( &mut self, src_vid: u32, offset: i32, ts: u64, ) -> Result<bool, StorageError>
Source§fn revert_delete_by_offset(
&mut self,
src_vid: u32,
offset: i32,
ts: u64,
) -> bool
fn revert_delete_by_offset( &mut self, src_vid: u32, offset: i32, ts: u64, ) -> bool
Source§fn nbr_at_offset(&self, src_vid: u32, offset: i32) -> Option<Nbr>
fn nbr_at_offset(&self, src_vid: u32, offset: i32) -> Option<Nbr>
Source§fn get_edge_physical(&self, src_vid: u32, dst: VertexId) -> Option<Nbr>
fn get_edge_physical(&self, src_vid: u32, dst: VertexId) -> Option<Nbr>
Source§fn physical_edges_of(&self, src_vid: u32) -> Vec<Nbr>
fn physical_edges_of(&self, src_vid: u32) -> Vec<Nbr>
Source§fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>)
fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>)
Source§fn has_physical_entries(&self, vid: u32) -> bool
fn has_physical_entries(&self, vid: u32) -> bool
Source§fn primary_contains(&self, src_vid: u32, edge_id: EdgeId) -> bool
fn primary_contains(&self, src_vid: u32, edge_id: EdgeId) -> bool
edge_id.Source§fn rollback_insert(&mut self, src_vid: u32, edge_id: EdgeId) -> bool
fn rollback_insert(&mut self, src_vid: u32, edge_id: EdgeId) -> bool
Source§fn revert_delete_by_edge_id(
&mut self,
src_vid: u32,
edge_id: EdgeId,
ts: u64,
) -> bool
fn revert_delete_by_edge_id( &mut self, src_vid: u32, edge_id: EdgeId, ts: u64, ) -> bool
ts. Read moreSource§fn get_edge(&self, src_vid: u32, dst: VertexId, ts: u64) -> Option<Nbr>
fn get_edge(&self, src_vid: u32, dst: VertexId, ts: u64) -> Option<Nbr>
Source§fn edges_of(&self, src_vid: u32, ts: u64) -> Vec<Nbr>
fn edges_of(&self, src_vid: u32, ts: u64) -> Vec<Nbr>
Source§fn compact_vertex_with_reporting(
&mut self,
vid: u32,
cutoff: u64,
on_edge_removed: &mut dyn FnMut(EdgeId, u64),
) -> usize
fn compact_vertex_with_reporting( &mut self, vid: u32, cutoff: u64, on_edge_removed: &mut dyn FnMut(EdgeId, u64), ) -> usize
cutoff and tightening live entries without touching other rows. Read moreSource§fn reclaimable_count(&self, vid: u32, cutoff: u64) -> usize
fn reclaimable_count(&self, vid: u32, cutoff: u64) -> usize
cutoff. Read moreSource§fn vertex_needs_compact(&self, vid: u32, cutoff: u64) -> bool
fn vertex_needs_compact(&self, vid: u32, cutoff: u64) -> bool
cutoff.Source§fn vertex_census(&self, vid: u32) -> (usize, usize, usize)
fn vertex_census(&self, vid: u32) -> (usize, usize, usize)
(live, dead, capacity). Read moreSource§fn vertex_reclaim_probe(&self, vid: u32, cutoff: u64) -> (usize, usize)
fn vertex_reclaim_probe(&self, vid: u32, cutoff: u64) -> (usize, usize)
(dead, reclaimable). Read moreSource§fn row_gap(&self, vid: u32) -> usize
fn row_gap(&self, vid: u32) -> usize
Source§fn row_density(&self, vid: u32) -> f32
fn row_density(&self, vid: u32) -> f32
Source§fn rebalance_row(&mut self, vid: u32) -> bool
fn rebalance_row(&mut self, vid: u32) -> bool
Source§fn used_memory_size(&self) -> usize
fn used_memory_size(&self) -> usize
Auto Trait Implementations§
impl Freeze for MutableCsr
impl RefUnwindSafe for MutableCsr
impl Send for MutableCsr
impl Sync for MutableCsr
impl Unpin for MutableCsr
impl UnsafeUnpin for MutableCsr
impl UnwindSafe for MutableCsr
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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
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.