Skip to main content

InMemoryGraphMemory

Struct InMemoryGraphMemory 

Source
pub struct InMemoryGraphMemory<N, E>
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,
{ /* private fields */ }
Expand description

In-process GraphMemory backed by BTreeMap adjacency lists, sharded per namespace.

Locking model:

  • The outer DashMap keys per-namespace tables. DashMap shards the map across N internal stripes so insertions and lookups across distinct namespaces never serialise on a single mutex.
  • Each table sits behind its own RwLock. Writes to one namespace block writes to that namespace only; concurrent writes against distinct namespaces run in parallel.
  • Reads against a namespace share the read side of the per-table RwLock, so dashboards / agents querying the same graph in parallel scale linearly until contention on a single tenant’s write rate.

Cheap to clone — internal state is Arc<DashMap<...>>-shared, so every clone observes the same graph.

Implementations§

Source§

impl<N, E> InMemoryGraphMemory<N, E>
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,

Source

pub fn new() -> Self

Empty graph. Cheap to clone.

Source

pub fn total_nodes(&self) -> usize

Total node count across all namespaces — useful for tests. Iterates DashMap entries (each acquired via its own shard lock) and reads each per-namespace lock independently.

Source

pub fn total_edges(&self) -> usize

Total edge count across all namespaces — useful for tests.

Trait Implementations§

Source§

impl<N, E> Clone for InMemoryGraphMemory<N, E>
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<N, E> Default for InMemoryGraphMemory<N, E>
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,

Source§

fn default() -> Self

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

impl<N, E> GraphMemory<N, E> for InMemoryGraphMemory<N, E>
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,

Source§

fn add_node<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node: N, ) -> Pin<Box<dyn Future<Output = Result<NodeId>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Insert node and return its assigned id.
Source§

fn add_edge<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: &'life3 NodeId, to: &'life4 NodeId, edge: E, timestamp: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<EdgeId>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Insert an edge from from to to carrying edge. timestamp is supplied by the caller so re-inserting after a replay produces deterministic edges.
Source§

fn add_edges_batch<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edges: Vec<(NodeId, NodeId, E, DateTime<Utc>)>, ) -> Pin<Box<dyn Future<Output = Result<Vec<EdgeId>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Insert a batch of edges atomically. Each tuple is (from, to, edge, timestamp); endpoints must already exist (same contract as Self::add_edge). Returns the assigned EdgeIds in input order. Read more
Source§

fn get_node<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, id: &'life3 NodeId, ) -> Pin<Box<dyn Future<Output = Result<Option<N>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Look up a node by id (verb-family get per .claude/rules/naming.md — single-item primary-key lookup).
Source§

fn get_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edge_id: &'life3 EdgeId, ) -> Pin<Box<dyn Future<Output = Result<Option<GraphHop<E>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Look up an edge by id and return the full structural body (GraphHop<E>from, to, edge, timestamp). Operators rarely want the payload alone for edges; the endpoints and timestamp are usually load-bearing for any follow-up decision (audit context, freshness check, neighbour navigation). Returning the full hop saves a second lookup. Read more
Source§

fn neighbors<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node: &'life3 NodeId, direction: Direction, ) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeId, NodeId, E)>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Edges incident to node in the requested direction. Each triple is (EdgeId, neighbour NodeId, edge payload).
Source§

fn traverse<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, start: &'life3 NodeId, direction: Direction, max_depth: usize, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Breadth-first traversal starting at start, expanding up to max_depth hops along edges in the requested direction. Returns the visited hops in BFS order (excluding the seed node, which has no inbound edge in this traversal). Use Direction::Both for relationship-graph queries that don’t care about edge polarity (knowledge graphs typically want this).
Source§

fn find_path<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: &'life3 NodeId, to: &'life4 NodeId, direction: Direction, max_depth: usize, ) -> Pin<Box<dyn Future<Output = Result<Option<Vec<GraphHop<E>>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Shortest unweighted path from from to to (BFS) along edges in the requested direction. Returns the sequence of hops; Some(vec![]) means from == to (already at destination — no edges traversed); None means no path exists within max_depth hops.
Source§

fn temporal_filter<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: DateTime<Utc>, to: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Edges whose timestamp falls in [from, to). Useful for audit-log style queries (“what relationships did the agent learn last week”).
Source§

fn node_count<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Count nodes in ns. Cheap operator metric for size-based decisions (paginate vs stream, fast-fail empty-namespace check, audit / dashboard surface). Default impl returns 0.
Source§

fn edge_count<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Count edges in ns. Cheap operator metric — same rationale as Self::node_count. Default impl returns 0.
Source§

fn delete_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edge_id: &'life3 EdgeId, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Drop one edge by id. Idempotent — deleting an absent edge succeeds. Required — backends that don’t support edge deletion are degenerate; closed the CRUD-completeness gap.
Source§

fn delete_node<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node_id: &'life3 NodeId, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Drop one node by id and every edge incident to it. Cascades — operators that don’t want cascading delete every incident edge first via Self::delete_edge and then call this. Returns the count of removed edges so callers can log or expose cleanup metrics; 0 when the node had no edges (or was absent — the operation is idempotent). Read more
Source§

fn prune_older_than<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, ttl: Duration, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Drop every edge in ns whose timestamp is older than ttl ago. Returns the count of removed edges so callers can log or expose pruning metrics. Read more

Auto Trait Implementations§

§

impl<N, E> Freeze for InMemoryGraphMemory<N, E>

§

impl<N, E> !RefUnwindSafe for InMemoryGraphMemory<N, E>

§

impl<N, E> Send for InMemoryGraphMemory<N, E>

§

impl<N, E> Sync for InMemoryGraphMemory<N, E>

§

impl<N, E> Unpin for InMemoryGraphMemory<N, E>

§

impl<N, E> UnsafeUnpin for InMemoryGraphMemory<N, E>

§

impl<N, E> !UnwindSafe for InMemoryGraphMemory<N, E>

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. 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