Skip to main content

StateStoreBackend

Trait StateStoreBackend 

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

Source

type Transaction: StateTransactionBackend

Unique owned transaction returned by this store.

Required Methods§

Source

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());
Source

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());
Source

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".

Implementors§