Skip to main content

ConcurrentEdgeStore

Struct ConcurrentEdgeStore 

Source
pub struct ConcurrentEdgeStore { /* private fields */ }
Expand description

A thread-safe edge store using sharded locking.

Distributes edges across multiple shards based on source node ID to reduce lock contention in multi-threaded scenarios.

§Cross-Shard Edge Storage Pattern

Edges that span different shards (source and target in different shards) are stored in BOTH shards:

  • Source shard: Full edge with outgoing + label indices (add_edge)
  • Target shard: Edge copy with incoming index only (add_edge_incoming_only)

§Lock Ordering

When acquiring multiple shard locks, always acquire in ascending shard index order to prevent deadlocks.

Implementations§

Source§

impl ConcurrentEdgeStore

Source

pub fn from_edge_store(store: &EdgeStore) -> Self

Builds a ConcurrentEdgeStore from a persisted EdgeStore.

Re-distributes edges across shards based on source node ID.

Issue #905: uses add_edges_batch (one edge_ids write-lock acquisition for the whole set, snapshot invalidated once) instead of a per-edge add_edge loop (one lock cycle + one snapshot-dirty flip per edge). The single build_read_snapshot at the end performs exactly one O(N+E) CSR build for the whole reconstruction.

Source

pub fn save_to_file(&self, path: &Path) -> Result<()>

Saves the concurrent edge store to a file.

§Errors

Returns an error if serialization or file I/O fails.

Source

pub fn load_from_file(path: &Path) -> Result<Self>

Loads a concurrent edge store from a persisted file.

§Errors

Returns an error if file I/O or deserialization fails.

Source§

impl ConcurrentEdgeStore

Source

pub fn get_outgoing(&self, node_id: u64) -> Vec<GraphEdge>

Gets all outgoing edges from a node (thread-safe).

Source

pub fn get_incoming(&self, node_id: u64) -> Vec<GraphEdge>

Gets all incoming edges to a node (thread-safe).

Source

pub fn get_neighbors(&self, node_id: u64) -> Vec<u64>

Gets neighbors (target nodes) of a given node.

When a CSR read snapshot is available (see build_read_snapshot()), this returns a copy from contiguous memory without resolving individual edges. Falls back to per-shard edge lookup otherwise.

Source

pub fn with_neighbors<F, R>(&self, node_id: u64, f: F) -> R
where F: FnOnce(&[u64]) -> R,

Invokes f with a borrowed slice of outgoing neighbor IDs.

When the CSR snapshot is available, f receives a zero-copy &[u64] from contiguous memory. Otherwise, a temporary Vec<u64> is built from per-shard edge lookup.

Prefer this over get_neighbors in tight loops (BFS frontiers) where the caller processes IDs inline.

Source

pub fn get_outgoing_by_label(&self, node_id: u64, label: &str) -> Vec<GraphEdge>

Gets outgoing edges filtered by label (thread-safe).

§Performance Note

This method delegates to the underlying EdgeStore::get_outgoing_by_label which uses the composite index (source_id, label) -> edge_ids for O(1) lookup when available (EPIC-019 US-003). Falls back to filtering if index not populated.

Source

pub fn get_incoming_by_label(&self, node_id: u64, label: &str) -> Vec<GraphEdge>

Gets incoming edges filtered by label (thread-safe).

Source

pub fn get_edges_by_label(&self, label: &str) -> Vec<GraphEdge>

Gets all edges with a specific label across all shards.

§Performance Warning

This method iterates through ALL shards and aggregates results. For large graphs with many shards, this can be expensive. Consider using get_outgoing_by_label(node_id, label) if you know the source node, which is O(k) instead of O(shards × edges_per_label).

Source

pub fn max_edge_id(&self) -> Option<u64>

Checks if an edge with the given ID exists. Returns the highest edge id in the store, if any.

O(edges) over the id registry — no edge cloning.

Source

pub fn contains_edge(&self, edge_id: u64) -> bool

Returns true when an edge with edge_id exists.

Source

pub fn get_edge(&self, edge_id: u64) -> Option<GraphEdge>

Gets an edge by ID using optimized source shard lookup.

Returns None if the edge doesn’t exist.

Source

pub fn traverse_bfs(&self, start: u64, max_depth: u32) -> Vec<u64>

Traverses the graph using BFS from a starting node.

Returns all nodes reachable within max_depth hops.

When a CSR read snapshot is available, neighbor lookups are zero-copy slices from contiguous memory. Otherwise uses Read-Copy-Drop pattern with per-shard locks.

Source

pub fn edge_count(&self) -> usize

Returns the total edge count across all shards.

Uses outgoing edge count to avoid double-counting edges that span shards.

Source

pub fn len(&self) -> usize

Returns len() — alias for edge_count() for API parity with EdgeStore.

Source

pub fn is_empty(&self) -> bool

Returns true if the store contains no edges.

Source

pub fn label_count(&self) -> usize

Returns the number of distinct edge labels in the graph.

Reads from the CSR snapshot’s interned label table, triggering a lazy rebuild if dirty. Returns 0 when the store has no edges.

Source

pub fn all_edges(&self) -> Vec<GraphEdge>

Returns all edges across all shards (cloned).

Uses the edge_ids registry to look up each edge exactly once in its source shard, avoiding double-counting for cross-shard edges.

§Performance Warning

Iterates all edges and clones each one. For large graphs, prefer targeted queries (get_outgoing, get_edges_by_label).

Source

pub fn outgoing_degree(&self, node_id: u64) -> usize

Returns the out-degree of a node without materializing edge vectors.

Uses CSR snapshot when available for O(1) lookup without shard locking.

Source

pub fn incoming_degree(&self, node_id: u64) -> usize

Returns the in-degree of a node without materializing edge vectors.

Source

pub fn get_csr_snapshot(&self) -> Guard<Arc<CsrSnapshot>>

Returns the current CSR snapshot (lock-free read).

The returned Guard dereferences to Arc<CsrSnapshot> and keeps the snapshot alive for the duration of the borrow. No locks are acquired — this is a single atomic load.

If the snapshot is dirty (mutation occurred since last rebuild), triggers a lazy rebuild before returning.

Source

pub fn traverse_bfs_csr( &self, source: u64, config: &TraversalConfig, ) -> Vec<TraversalResult>

BFS traversal on the CSR snapshot (lock-free, zero-copy).

Loads the current snapshot atomically and delegates to bfs_traverse_csr for the actual traversal. Triggers a lazy CSR rebuild if dirty.

Source

pub fn traverse_bfs_filtered<P: EdgePredicate>( &self, source: u64, config: &TraversalConfig, predicate: &P, ) -> Vec<TraversalResult>

BFS traversal with predicate pushdown on the CSR snapshot.

Loads the current snapshot atomically and delegates to bfs_traverse_csr_filtered which applies the predicate at the CSR level, avoiding materialisation of non-matching edges. Triggers a lazy CSR rebuild if dirty.

Source§

impl ConcurrentEdgeStore

Source

pub fn build_read_snapshot(&self)

Builds a CSR-like read snapshot from current shard state.

The snapshot stores only outgoing neighbor target node IDs per source node in contiguous memory, enabling with_neighbors() to provide zero-copy &[u64] access without shard locking.

§Limitation — target IDs only

The snapshot does not store edge IDs, labels, or properties. It is optimized for BFS neighbor expansion where only connectivity matters. To retrieve full edge metadata (edge ID, label, properties), use get_outgoing() which reads from the authoritative shard data.

Call this after bulk inserts, after flush(), or after loading from disk. The snapshot is automatically invalidated on any write.

Source

pub fn has_read_snapshot(&self) -> bool

Returns true if a CSR read snapshot is currently available.

Source§

impl ConcurrentEdgeStore

Source

pub fn new() -> Self

Creates a new concurrent edge store with the default number of shards.

Uses DEFAULT_NUM_SHARDS (compile-time constant > 0), so this constructor cannot fail in practice.

Source

pub fn with_shards(num_shards: usize) -> Result<Self>

Creates a new concurrent edge store with a specific number of shards.

§Errors

Returns Error::Config if num_shards is 0 (would cause division-by-zero in shard_index).

Source

pub fn metrics(&self) -> &GraphMetrics

Returns the operational metrics for this edge store.

Counters/histograms cover edge inserts, deletes, and traversals.

Source

pub fn with_estimated_edges(estimated_edges: usize) -> Self

Creates a concurrent edge store with optimal shard count for estimated edge count.

FLAG-6: Uses integer bit manipulation for ceiling log2.

Source

pub fn add_edge(&self, edge: GraphEdge) -> Result<()>

Adds an edge to the store (thread-safe).

Edges are stored in BOTH source and target shards:

  • Source shard: for outgoing index lookups
  • Target shard: for incoming index lookups

When source and target are in different shards, locks are acquired in ascending shard index order to prevent deadlocks.

§Errors

Returns Error::EdgeExists if an edge with the same ID already exists.

Source

pub fn add_edges_batch(&self, edges: Vec<GraphEdge>) -> usize

Adds multiple edges in batch with a single lock acquisition cycle.

Acquires the edge_ids write lock once for the entire batch, inserts all edges into their respective shards, then invalidates the CSR snapshot once at the end. This is 10-50x faster than calling add_edge in a loop for large batches.

Edges that already exist (duplicate IDs) are silently skipped.

§Returns

Number of edges successfully added.

Source

pub fn remove_edge(&self, edge_id: u64) -> bool

Removes an edge by ID using optimized 2-shard lookup.

§Concurrency Safety

Lock ordering: edge_ids FIRST, then shards in ascending order.

Source

pub fn remove_node_edges(&self, node_id: u64)

Removes all edges connected to a node (cascade delete, thread-safe).

§Concurrency Safety

Lock ordering: edge_ids FIRST, then shards in ascending order.

Trait Implementations§

Source§

impl Default for ConcurrentEdgeStore

Source§

fn default() -> Self

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

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

Source§

fn fg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the foreground set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like red() and green(), which have the same functionality but are pithier.

§Example

Set foreground color to white using fg():

use yansi::{Paint, Color};

painted.fg(Color::White);

Set foreground color to white using white().

use yansi::Paint;

painted.white();
Source§

fn primary(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Primary].

§Example
println!("{}", value.primary());
Source§

fn fixed(&self, color: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Fixed].

§Example
println!("{}", value.fixed(color));
Source§

fn rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Rgb].

§Example
println!("{}", value.rgb(r, g, b));
Source§

fn black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Black].

§Example
println!("{}", value.black());
Source§

fn red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Red].

§Example
println!("{}", value.red());
Source§

fn green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Green].

§Example
println!("{}", value.green());
Source§

fn yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Yellow].

§Example
println!("{}", value.yellow());
Source§

fn blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Blue].

§Example
println!("{}", value.blue());
Source§

fn magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Magenta].

§Example
println!("{}", value.magenta());
Source§

fn cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Cyan].

§Example
println!("{}", value.cyan());
Source§

fn white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: White].

§Example
println!("{}", value.white());
Source§

fn bright_black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlack].

§Example
println!("{}", value.bright_black());
Source§

fn bright_red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightRed].

§Example
println!("{}", value.bright_red());
Source§

fn bright_green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightGreen].

§Example
println!("{}", value.bright_green());
Source§

fn bright_yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightYellow].

§Example
println!("{}", value.bright_yellow());
Source§

fn bright_blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlue].

§Example
println!("{}", value.bright_blue());
Source§

fn bright_magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.bright_magenta());
Source§

fn bright_cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightCyan].

§Example
println!("{}", value.bright_cyan());
Source§

fn bright_white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightWhite].

§Example
println!("{}", value.bright_white());
Source§

fn bg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the background set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like on_red() and on_green(), which have the same functionality but are pithier.

§Example

Set background color to red using fg():

use yansi::{Paint, Color};

painted.bg(Color::Red);

Set background color to red using on_red().

use yansi::Paint;

painted.on_red();
Source§

fn on_primary(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Primary].

§Example
println!("{}", value.on_primary());
Source§

fn on_fixed(&self, color: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Fixed].

§Example
println!("{}", value.on_fixed(color));
Source§

fn on_rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Rgb].

§Example
println!("{}", value.on_rgb(r, g, b));
Source§

fn on_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Black].

§Example
println!("{}", value.on_black());
Source§

fn on_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Red].

§Example
println!("{}", value.on_red());
Source§

fn on_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Green].

§Example
println!("{}", value.on_green());
Source§

fn on_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Yellow].

§Example
println!("{}", value.on_yellow());
Source§

fn on_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Blue].

§Example
println!("{}", value.on_blue());
Source§

fn on_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Magenta].

§Example
println!("{}", value.on_magenta());
Source§

fn on_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Cyan].

§Example
println!("{}", value.on_cyan());
Source§

fn on_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: White].

§Example
println!("{}", value.on_white());
Source§

fn on_bright_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlack].

§Example
println!("{}", value.on_bright_black());
Source§

fn on_bright_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightRed].

§Example
println!("{}", value.on_bright_red());
Source§

fn on_bright_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightGreen].

§Example
println!("{}", value.on_bright_green());
Source§

fn on_bright_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightYellow].

§Example
println!("{}", value.on_bright_yellow());
Source§

fn on_bright_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlue].

§Example
println!("{}", value.on_bright_blue());
Source§

fn on_bright_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.on_bright_magenta());
Source§

fn on_bright_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightCyan].

§Example
println!("{}", value.on_bright_cyan());
Source§

fn on_bright_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightWhite].

§Example
println!("{}", value.on_bright_white());
Source§

fn attr(&self, value: Attribute) -> Painted<&T>

Enables the styling Attribute value.

This method should be used rarely. Instead, prefer to use attribute-specific builder methods like bold() and underline(), which have the same functionality but are pithier.

§Example

Make text bold using attr():

use yansi::{Paint, Attribute};

painted.attr(Attribute::Bold);

Make text bold using using bold().

use yansi::Paint;

painted.bold();
Source§

fn bold(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Bold].

§Example
println!("{}", value.bold());
Source§

fn dim(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Dim].

§Example
println!("{}", value.dim());
Source§

fn italic(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Italic].

§Example
println!("{}", value.italic());
Source§

fn underline(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Underline].

§Example
println!("{}", value.underline());

Returns self with the attr() set to [Attribute :: Blink].

§Example
println!("{}", value.blink());

Returns self with the attr() set to [Attribute :: RapidBlink].

§Example
println!("{}", value.rapid_blink());
Source§

fn invert(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Invert].

§Example
println!("{}", value.invert());
Source§

fn conceal(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Conceal].

§Example
println!("{}", value.conceal());
Source§

fn strike(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Strike].

§Example
println!("{}", value.strike());
Source§

fn quirk(&self, value: Quirk) -> Painted<&T>

Enables the yansi Quirk value.

This method should be used rarely. Instead, prefer to use quirk-specific builder methods like mask() and wrap(), which have the same functionality but are pithier.

§Example

Enable wrapping using .quirk():

use yansi::{Paint, Quirk};

painted.quirk(Quirk::Wrap);

Enable wrapping using wrap().

use yansi::Paint;

painted.wrap();
Source§

fn mask(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Mask].

§Example
println!("{}", value.mask());
Source§

fn wrap(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Wrap].

§Example
println!("{}", value.wrap());
Source§

fn linger(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Linger].

§Example
println!("{}", value.linger());
Source§

fn clear(&self) -> Painted<&T>

👎Deprecated since 1.0.1:

renamed to resetting() due to conflicts with Vec::clear(). The clear() method will be removed in a future release.

Returns self with the quirk() set to [Quirk :: Clear].

§Example
println!("{}", value.clear());
Source§

fn resetting(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Resetting].

§Example
println!("{}", value.resetting());
Source§

fn bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Bright].

§Example
println!("{}", value.bright());
Source§

fn on_bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: OnBright].

§Example
println!("{}", value.on_bright());
Source§

fn whenever(&self, value: Condition) -> Painted<&T>

Conditionally enable styling based on whether the Condition value applies. Replaces any previous condition.

See the crate level docs for more details.

§Example

Enable styling painted only when both stdout and stderr are TTYs:

use yansi::{Paint, Condition};

painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);
Source§

fn new(self) -> Painted<Self>
where Self: Sized,

Create a new Painted with a default Style. Read more
Source§

fn paint<S>(&self, style: S) -> Painted<&Self>
where S: Into<Style>,

Apply a style wholesale to self. Any previous style is replaced. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

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

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more