pub trait DatabaseBackend:
Send
+ Sync
+ 'static {
// Required methods
fn capabilities(&self) -> CapabilitySet;
fn catalog(
&self,
request: CatalogRequest,
) -> impl Future<Output = Result<CatalogResponse, StorageError>> + Send + '_;
fn data(
&self,
request: DataRequest,
) -> impl Future<Output = Result<DataResponse, StorageError>> + Send + '_;
fn query(
&self,
request: QueryRequest,
) -> impl Future<Output = Result<QueryResponse, StorageError>> + Send + '_;
fn index(
&self,
request: IndexRequest,
) -> impl Future<Output = Result<IndexResponse, StorageError>> + Send + '_;
}Expand description
Portable data, catalog, query, and index behavior.
Implement this trait directly on a caller-owned type; Clone is not
required. Operations are authoritative: an optional capability bit does not
guarantee that an arbitrary request is supported. Unsupported operations
fail before mutation, and responses preserve real ids, counts and score
semantics. Supporting DTOs live in crate::database.
§Caller-owned catalog example
This small test double validates one request and returns an empty catalog. It demonstrates direct trait consumption, not a database engine. Other operations reject explicitly; no native storage or service is constructed.
use storage_kit::{DatabaseBackend, StorageError};
use storage_kit::database::CapabilitySet;
use storage_kit::database::catalog::{CatalogRequest, CatalogResponse, ListCollectionsRequest};
struct EmptyCatalog;
impl DatabaseBackend for EmptyCatalog {
fn capabilities(&self) -> CapabilitySet { CapabilitySet::empty() }
async fn catalog(&self, request: CatalogRequest) -> Result<CatalogResponse, StorageError> {
assert_eq!(request, CatalogRequest::ListCollections(ListCollectionsRequest {
namespace: Vec::new(), limit: Some(10), start_after: None,
}));
Ok(CatalogResponse::Collections(Vec::new()))
}
}
let database = EmptyCatalog;
let response = database.catalog(CatalogRequest::ListCollections(ListCollectionsRequest {
namespace: Vec::new(), limit: Some(10), start_after: None,
})).await?;
let CatalogResponse::Collections(collections) = response else {
panic!("expected the catalog response");
};
assert!(collections.is_empty());
assert!(database.capabilities().is_empty());An application may compose different implementations in its own exhaustive enum, forwarding requests and errors unchanged. See the executable caller composition tests. The local collection workflow uses real embedded storage.
Required Methods§
Sourcefn capabilities(&self) -> CapabilitySet
fn capabilities(&self) -> CapabilitySet
Returns optional retained capabilities.
Use this to discover optional operation families, not to identify the implementation or assume an arbitrary request will succeed.
See the caller-owned catalog example for an executable example.
Sourcefn catalog(
&self,
request: CatalogRequest,
) -> impl Future<Output = Result<CatalogResponse, StorageError>> + Send + '_
fn catalog( &self, request: CatalogRequest, ) -> impl Future<Output = Result<CatalogResponse, StorageError>> + Send + '_
Executes a retained catalog operation.
Creation, opening, listing, deletion, and schema evolution remain distinct from data writes and StateStore transactions.
§Errors
Returns StorageError for invalid scope or request data, unsupported
operations, and native connection, deadline, or execution failures. An
unsupported operation must fail before mutation.
See the caller-owned catalog example for an executable example.
Sourcefn data(
&self,
request: DataRequest,
) -> impl Future<Output = Result<DataResponse, StorageError>> + Send + '_
fn data( &self, request: DataRequest, ) -> impl Future<Output = Result<DataResponse, StorageError>> + Send + '_
Executes a retained data operation.
Ordinary writes are not rollback-capable multi-request transactions. A native failure or timeout can leave an uncertain or partial outcome and must not trigger automatic replay. Partition replacement retains its explicit atomic contract.
§Errors
Returns StorageError for invalid input, unsupported request combinations,
read-only policy, serialization failures, and native execution/deadline
failures. Unsupported behavior must be rejected before mutation.
See the local collection workflow for an executable example.
Sourcefn query(
&self,
request: QueryRequest,
) -> impl Future<Output = Result<QueryResponse, StorageError>> + Send + '_
fn query( &self, request: QueryRequest, ) -> impl Future<Output = Result<QueryResponse, StorageError>> + Send + '_
Executes a retained query operation.
Return continuation tokens unchanged to the same applicable operation. Scores preserve their explicit kind; a missing score is not zero.
§Errors
Returns StorageError for invalid scope, vectors, fields, filters, or
tuning, unsupported query combinations, and native execution/deadline
failures. Optional capability bits do not override request validation.
§Examples
use storage_kit::{Database, DatabaseBackend};
use storage_kit::database::config::{DatabaseConfig, LanceConfig};
use storage_kit::database::StorageLocator;
use storage_kit::database::catalog::{CatalogRequest, CreateCollectionRequest, CreateCollectionMode};
use storage_kit::database::data::DataRecord;
use storage_kit::database::query::{QueryRequest, ScrollRequest, Projection};
let database = Database::connect(DatabaseConfig::lance(LanceConfig::embedded_path("memory://"))).await?;
let locator = StorageLocator::new("documents");
database.catalog(CatalogRequest::CreateCollection(CreateCollectionRequest {
locator: locator.clone(), schema: None,
records: vec![DataRecord::without_id().json_field("text", serde_json::json!("example"))],
mode: CreateCollectionMode::Create,
})).await?;
let response = database.query(QueryRequest::Scroll(ScrollRequest {
locator, filter: None, projection: Projection::All, limit: Some(10),
offset: None, page_token: None,
})).await?;
assert_eq!(response.hits.len(), 1);Sourcefn index(
&self,
request: IndexRequest,
) -> impl Future<Output = Result<IndexResponse, StorageError>> + Send + '_
fn index( &self, request: IndexRequest, ) -> impl Future<Output = Result<IndexResponse, StorageError>> + Send + '_
Executes a retained index operation.
Creating an index and observing its readiness are separate operations. Readiness requests must finish within both their requested wait bound and the enclosing configured operation deadline.
§Errors
Returns StorageError for malformed index requests, unsupported kinds,
read-only policy, missing targets, unconfirmed completion, or native
execution/readiness deadline failures.
§Examples
use storage_kit::{Database, DatabaseBackend};
use storage_kit::database::config::{DatabaseConfig, LanceConfig};
use storage_kit::database::StorageLocator;
use storage_kit::database::catalog::{CatalogRequest, CreateCollectionRequest, CreateCollectionMode};
use storage_kit::database::data::DataRecord;
use storage_kit::database::index::{IndexRequest, IndexResponse};
let database = Database::connect(DatabaseConfig::lance(LanceConfig::embedded_path("memory://"))).await?;
let locator = StorageLocator::new("documents");
database.catalog(CatalogRequest::CreateCollection(CreateCollectionRequest {
locator: locator.clone(), schema: None,
records: vec![DataRecord::without_id().json_field("text", serde_json::json!("example"))],
mode: CreateCollectionMode::Create,
})).await?;
let response = database.index(IndexRequest::List { locator }).await?;
assert!(matches!(response, IndexResponse::Indexes(_)));Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".