pub trait GraphStorageClientV1: Send + Sync {
// Required methods
fn register_types<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
batch: Vec<TypeRegistration>,
) -> Pin<Box<dyn Future<Output = Result<Vec<TypeRecord>, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn get_type<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
type_id: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<TypeRecord, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn list_types<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
query: TypeQuery,
) -> Pin<Box<dyn Future<Output = Result<Page<TypeRecord>, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn ingest<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: IngestRequest,
) -> Pin<Box<dyn Future<Output = Result<IngestOutcome, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn delete_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
node_key: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<DeleteOutcome, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn delete_edge<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
edge_key: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<DeleteOutcome, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn get_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
node_key: &'life2 String,
adjacency_limit: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<NodeView, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn project_nodes<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
type_patterns: &'life2 [String],
query: ODataQuery,
) -> Pin<Box<dyn Future<Output = Result<Page<NodeRow>, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn search<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: SearchRequest,
) -> Pin<Box<dyn Future<Output = Result<SearchResponse, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn traverse<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: TraverseRequest,
) -> Pin<Box<dyn Future<Output = Result<TraversalResponse, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn neighborhood<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: NeighborhoodRequest,
) -> Pin<Box<dyn Future<Output = Result<TraversalResponse, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn revision<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
) -> Pin<Box<dyn Future<Output = Result<GraphRevision, CanonicalError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
}Expand description
Object-safe client for in-process consumption by other gears (version 1).
§Errors
Every method returns the same CanonicalError taxonomy the REST surface
renders (DESIGN § Error Model), and the same category for the same
failure, because both adapters call one service. Documented once here
rather than per method: the vocabulary is the contract, and repeating it
twelve times would let the copies drift.
invalid_argument— a malformed request, a per-item schema violation (SCHEMA_VIOLATION, addressed by JSON pointer), a request the gear cannot interpret (INVALID_ARGUMENT), or two bounds that cannot hold at once (LIMIT_COMBINATION).out_of_range(LIMIT_EXCEEDED) — a value outside a documented hard range: batch size, depth, page size, an oversized key or query.not_found— the row is absent or the caller may not see it. The two are indistinguishable by contract (anti-enumeration), so a client must not read absence as permission to create.permission_denied(SOURCE_NAMESPACE_FORBIDDEN) — the one denial that names itself, because the caller wrote under a source namespace another producer owns, and that owner is a fact about the tenant rather than about them.aborted—CAS_CONFLICT(a same-key type change, a staleexpected_version, a scope owned by another producer),SERIALIZATION, orIDEMPOTENCY_MISMATCH. Re-read and retry.failed_precondition—STALE_GENERATION,IDEMPOTENCY_KEY_EXPIRED,SCOPE_UNSERVABLE,EMBEDDING_SPACE_MISMATCH. Not retryable unchanged.unavailable— a dependency is down: the PDP, the database, the embedding provider. Retry later.deadline_exceeded,cancelled— the operation ran out of the budget it started with, or the caller went away.unimplemented— a capability the selected engine or store does not provide (traversal on an engine without it, labels, topology).unknown,data_loss— an unexpected internal failure, or detected corruption. Escalate rather than retry.
Required Methods§
Sourcefn register_types<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
batch: Vec<TypeRegistration>,
) -> Pin<Box<dyn Future<Output = Result<Vec<TypeRecord>, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn register_types<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
batch: Vec<TypeRegistration>,
) -> Pin<Box<dyn Future<Output = Result<Vec<TypeRecord>, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Register a batch of GTS types, atomically. Byte-identical re-registration converges; a different schema for a registered identifier conflicts.
fn get_type<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
type_id: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<TypeRecord, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Sourcefn list_types<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
query: TypeQuery,
) -> Pin<Box<dyn Future<Output = Result<Page<TypeRecord>, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn list_types<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
query: TypeQuery,
) -> Pin<Box<dyn Future<Output = Result<Page<TypeRecord>, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
One page of the type catalogue.
Continue while next_cursor is Some, even when items is empty.
This endpoint does not follow the common “stop when the page is empty”
convention. A pattern is applied after rows are read, and the scan
gives up its pass after a bounded number of rows; when no row in that
pass matches, the answer is an empty page carrying the cursor to resume
from. An empty page therefore means “nothing here yet”, not “nothing
left” – only a next_cursor of None means that. A client that stops
on the empty page silently drops every match beyond it, which is most
likely exactly where a selective pattern finds them.
Sourcefn ingest<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: IngestRequest,
) -> Pin<Box<dyn Future<Output = Result<IngestOutcome, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn ingest<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: IngestRequest,
) -> Pin<Box<dyn Future<Output = Result<IngestOutcome, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Apply one atomic ingest batch. request.idempotency_key carries the
same value the REST path reads from the Idempotency-Key header.
The key is optional, and it is what makes a retry safe after an unknown commit outcome – the case where the batch committed and the response was lost. With a key, an identical retry returns the recorded outcome and touches no graph state; the same key with a different request is a conflict. Without one, none of that happens: no receipt is read and none is written, so a retry is a new logical request that re-runs the write path. That is not always harmless – a batch that replaces a scope removes what it does not re-declare, and running it twice is not the same as running it once. A producer that retries on timeout should send a key.
Sourcefn delete_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
node_key: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<DeleteOutcome, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn delete_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
node_key: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<DeleteOutcome, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Soft-delete a node together with its incident edges.
Sourcefn delete_edge<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
edge_key: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<DeleteOutcome, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn delete_edge<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
edge_key: &'life2 String,
) -> Pin<Box<dyn Future<Output = Result<DeleteOutcome, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Soft-delete one edge.
Sourcefn get_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
node_key: &'life2 String,
adjacency_limit: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<NodeView, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn get_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
node_key: &'life2 String,
adjacency_limit: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<NodeView, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Node by key with payload and bounded bidirectional adjacency.
adjacency_limit = None uses the configured default.
Sourcefn project_nodes<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
type_patterns: &'life2 [String],
query: ODataQuery,
) -> Pin<Box<dyn Future<Output = Result<Page<NodeRow>, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn project_nodes<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
type_patterns: &'life2 [String],
query: ODataQuery,
) -> Pin<Box<dyn Future<Output = Result<Page<NodeRow>, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Tabular projection over declared index paths, bound to the platform
OData options.
type_patterns narrows the projection to the types they resolve to;
the effective set is that intersected with the pattern of the
permission that authorized the request. Empty means every authorized
type. Patterns are resolved by the shared GTS implementation, never
compiled into SQL.
Sourcefn search<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: SearchRequest,
) -> Pin<Box<dyn Future<Output = Result<SearchResponse, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn search<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: SearchRequest,
) -> Pin<Box<dyn Future<Output = Result<SearchResponse, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Lexical, vector or hybrid search.
Sourcefn traverse<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: TraverseRequest,
) -> Pin<Box<dyn Future<Output = Result<TraversalResponse, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn traverse<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: TraverseRequest,
) -> Pin<Box<dyn Future<Output = Result<TraversalResponse, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Seeded, depth-bounded traversal.
Sourcefn neighborhood<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: NeighborhoodRequest,
) -> Pin<Box<dyn Future<Output = Result<TraversalResponse, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn neighborhood<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
request: NeighborhoodRequest,
) -> Pin<Box<dyn Future<Output = Result<TraversalResponse, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Bounded neighborhood projection.
Sourcefn revision<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
) -> Pin<Box<dyn Future<Output = Result<GraphRevision, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn revision<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SecurityContext,
) -> Pin<Box<dyn Future<Output = Result<GraphRevision, CanonicalError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
The caller-visible (source_epoch, graph_revision) identity.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".