pub trait StateStoreBackend:
Send
+ Sync
+ 'static {
type Transaction: StateTransactionBackend;
// Required methods
fn load(
&self,
key: StateKey,
) -> impl Future<Output = Result<Option<StateRecordSnapshot>, StateStoreError>> + Send + '_;
fn list(
&self,
request: StateListRequest,
) -> impl Future<Output = Result<StateListResponse, StateStoreError>> + Send + '_;
fn begin_transaction(
&self,
) -> impl Future<Output = Result<Self::Transaction, StateStoreError>> + Send + '_;
}Expand description
Transactional control-plane storage with caller-owned associated transactions.
Implement this trait directly; the backend need not implement Clone.
A direct Self::load or Self::list observes committed data but is not
a fence for a later mutation. Conditional writes must read and validate
their preconditions inside the same associated transaction that commits
the writes. App and Agent own their record schemas, histories and receipts.
§Examples
use storage_kit::{StateStore, StateStoreBackend, StateTransactionBackend};
use storage_kit::state::config::{StateStoreConfig, LanceStateStoreConfig};
use storage_kit::state::record::{StateKey, StateRecord};
let store = StateStore::connect(StateStoreConfig::lance(LanceStateStoreConfig::memory())).await?;
let key = StateKey::new("workflow", "run/1")?;
let mut transaction = store.begin_transaction().await?;
transaction.put(StateRecord::new(key.clone(), serde_json::json!({"status": "ready"}))).await?;
transaction.commit().await?;
assert!(store.load(key).await?.is_some());§Caller-owned store and transaction enums
This complete composition keeps real embedded-Lance semantics. The caller’s
ApplicationStore selects an application role; its associated Transaction
selects the matching child. Neither enum implements Clone. Commit and rollback
consume the transaction and delegate finalization to its actual owner. This is
an illustrative caller composition, not an alternative transaction engine.
use storage_kit::{StateStore, StateStoreBackend, StateTransaction, StateTransactionBackend, StateStoreError};
use storage_kit::state::config::{StateStoreConfig, LanceStateStoreConfig};
use storage_kit::state::record::{StateKey, StateRecord, StateRecordSnapshot, StateListRequest, StateListResponse};
enum ApplicationStore { Live(StateStore), Scratch(StateStore) }
enum Transaction { Live(StateTransaction), Scratch(StateTransaction) }
impl StateStoreBackend for ApplicationStore {
type Transaction = Transaction;
async fn load(&self, key: StateKey) -> Result<Option<StateRecordSnapshot>, StateStoreError> {
match self { Self::Live(store) => store.load(key).await, Self::Scratch(store) => store.load(key).await }
}
async fn list(&self, request: StateListRequest) -> Result<StateListResponse, StateStoreError> {
match self { Self::Live(store) => store.list(request).await, Self::Scratch(store) => store.list(request).await }
}
async fn begin_transaction(&self) -> Result<Transaction, StateStoreError> {
match self {
Self::Live(store) => Ok(Transaction::Live(store.begin_transaction().await?)),
Self::Scratch(store) => Ok(Transaction::Scratch(store.begin_transaction().await?)),
}
}
}
impl StateTransactionBackend for Transaction {
async fn load(&mut self, key: StateKey) -> Result<Option<StateRecordSnapshot>, StateStoreError> {
match self { Self::Live(tx) => tx.load(key).await, Self::Scratch(tx) => tx.load(key).await }
}
async fn put(&mut self, record: StateRecord) -> Result<(), StateStoreError> {
match self { Self::Live(tx) => tx.put(record).await, Self::Scratch(tx) => tx.put(record).await }
}
async fn delete(&mut self, key: StateKey) -> Result<(), StateStoreError> {
match self { Self::Live(tx) => tx.delete(key).await, Self::Scratch(tx) => tx.delete(key).await }
}
async fn commit(self) -> Result<(), StateStoreError> {
match self { Self::Live(tx) => tx.commit().await, Self::Scratch(tx) => tx.commit().await }
}
async fn rollback(self) -> Result<(), StateStoreError> {
match self { Self::Live(tx) => tx.rollback().await, Self::Scratch(tx) => tx.rollback().await }
}
}
let live = ApplicationStore::Live(StateStore::connect(
StateStoreConfig::lance(LanceStateStoreConfig::memory()),
).await?);
let scratch = ApplicationStore::Scratch(StateStore::connect(
StateStoreConfig::lance(LanceStateStoreConfig::memory()),
).await?);
let key = StateKey::new("jobs", "one")?;
let mut accepted = live.begin_transaction().await?;
accepted.put(StateRecord::new(key.clone(), serde_json::json!("ready"))).await?;
accepted.commit().await?;
assert_eq!(live.load(key.clone()).await?.unwrap().revision.get(), 1);
let mut discarded = scratch.begin_transaction().await?;
discarded.put(StateRecord::new(key.clone(), serde_json::json!("draft"))).await?;
assert!(discarded.load(key.clone()).await?.is_some());
discarded.rollback().await?;
assert!(scratch.load(key).await?.is_none());
drop((live, scratch));Required Associated Types§
Sourcetype Transaction: StateTransactionBackend
type Transaction: StateTransactionBackend
Unique owned transaction returned by this store.
Required Methods§
Sourcefn load(
&self,
key: StateKey,
) -> impl Future<Output = Result<Option<StateRecordSnapshot>, StateStoreError>> + Send + '_
fn load( &self, key: StateKey, ) -> impl Future<Output = Result<Option<StateRecordSnapshot>, StateStoreError>> + Send + '_
Loads the current non-deleted value for a key.
§Errors
Returns the owning StateStoreError when the read cannot complete,
including unavailable storage, timeout, or record decoding failure.
§Examples
use storage_kit::{StateStore, StateStoreBackend, StateTransactionBackend};
use storage_kit::state::config::{StateStoreConfig, LanceStateStoreConfig};
use storage_kit::state::record::StateKey;
let store = StateStore::connect(StateStoreConfig::lance(LanceStateStoreConfig::memory())).await?;
let key = StateKey::new("workflow", "run/1")?;
assert!(store.load(key).await?.is_none());Sourcefn list(
&self,
request: StateListRequest,
) -> impl Future<Output = Result<StateListResponse, StateStoreError>> + Send + '_
fn list( &self, request: StateListRequest, ) -> impl Future<Output = Result<StateListResponse, StateStoreError>> + Send + '_
Lists present records in canonical ascending UTF-8 key order.
Namespace and prefix matching are exact and case-sensitive; wildcard characters in a prefix are literal. The cursor is exclusive. A continuation is the last returned key only when another matching record exists in this page’s view. Separate pages are not a snapshot; callers must fence/revalidate inventories that can change concurrently.
§Errors
Returns the owning StateStoreError when the page cannot be read or
decoded, or the configured operation deadline expires.
§Examples
use storage_kit::{StateStore, StateStoreBackend, StateTransactionBackend};
use storage_kit::state::config::{StateStoreConfig, LanceStateStoreConfig};
use storage_kit::state::record::{StateKey, StateRecord, StateListRequest};
let store = StateStore::connect(StateStoreConfig::lance(LanceStateStoreConfig::memory())).await?;
let key = StateKey::new("workflow", "run/1")?;
let mut transaction = store.begin_transaction().await?;
transaction.put(StateRecord::new(key, serde_json::json!(42))).await?;
transaction.commit().await?;
let page = store.list(StateListRequest::new("workflow")?.with_limit(10)?).await?;
assert_eq!(page.records.len(), 1);
assert!(page.next_start_after.is_none());Sourcefn begin_transaction(
&self,
) -> impl Future<Output = Result<Self::Transaction, StateStoreError>> + Send + '_
fn begin_transaction( &self, ) -> impl Future<Output = Result<Self::Transaction, StateStoreError>> + Send + '_
Opens an owned transaction whose successful outcomes are serializable.
Admission may wait or subsequent operations may report an exact
StateStoreError::TransactionConflict. The returned transaction’s
reads, including absent keys, participate in its serializable outcome.
The native transaction may begin lazily at the first operation.
§Errors
Returns StateStoreError if admission or native transaction setup fails,
times out, or encounters a serialization conflict.
§Examples
use storage_kit::{StateStore, StateStoreBackend, StateTransactionBackend};
use storage_kit::state::config::{StateStoreConfig, LanceStateStoreConfig};
let store = StateStore::connect(StateStoreConfig::lance(LanceStateStoreConfig::memory())).await?;
let transaction = store.begin_transaction().await?;
transaction.rollback().await?;Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".