1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Revisioned control-plane state and owned transactions.
//!
//! Open a supplied [`crate::StateStore`] from [`config::StateStoreConfig`],
//! import [`crate::StateStoreBackend`] for reads and transaction creation, and
//! import [`crate::StateTransactionBackend`] to stage and finalize mutations.
//! [`record`] contains validated identities, write inputs, observations, and
//! pagination. Transactions remain unique, lifetime-free values and are
//! consumed by commit or rollback.
//!
//! Read conditional-write preconditions inside the committing transaction.
//! Ordinary reads and separate listing pages do not establish a snapshot.
//! A lost commit reply may require durable reconciliation before retrying.
//! # Preconditions, revisions and pagination
//!
//! This runs on embedded Lance without a service. Conditional writes validate their
//! precondition inside the same transaction. Rollback consumes the transaction and
//! discards its staged revision. Do not automatically retry an ambiguous commit.
//!
//! ```
//! use storage_kit::{StateStore, StateStoreBackend, StateTransactionBackend};
//! use storage_kit::state::config::{LanceStateStoreConfig, StateStoreConfig};
//! use storage_kit::state::record::{StateKey, StateListRequest, StateRecord};
//! # tokio::runtime::Runtime::new()?.block_on(async {
//! let store = StateStore::connect(StateStoreConfig::lance(LanceStateStoreConfig::memory())).await?;
//! let key = StateKey::new("jobs", "a")?;
//! let mut create = store.begin_transaction().await?;
//! assert!(create.load(key.clone()).await?.is_none());
//! create.put(StateRecord::new(key.clone(), serde_json::json!({"status":"ready"}))).await?;
//! create.put(StateRecord::new(StateKey::new("jobs", "b")?, serde_json::json!({"status":"ready"}))).await?;
//! create.commit().await?;
//!
//! let mut update = store.begin_transaction().await?;
//! let before = update.load(key.clone()).await?.expect("created job");
//! assert_eq!(before.revision.get(), 1);
//! assert_eq!(before.record.value["status"], "ready");
//! update.put(StateRecord::new(key.clone(), serde_json::json!({"status":"running"}))).await?;
//! assert_eq!(update.load(key.clone()).await?.unwrap().revision.get(), 2);
//! update.rollback().await?;
//! assert_eq!(store.load(key.clone()).await?.unwrap().revision.get(), 1);
//!
//! let first = store.list(StateListRequest::new("jobs")?.with_limit(1)?).await?;
//! assert_eq!(first.records.len(), 1);
//! let cursor = first.next_start_after.expect("one remaining job");
//! let second = store.list(StateListRequest::new("jobs")?.with_limit(1)?.start_after(cursor)?).await?;
//! assert_eq!(second.records.len(), 1);
//! assert!(second.next_start_after.is_none());
//! // Separate pages observe committed state independently, not one frozen snapshot.
//! let mut replace = store.begin_transaction().await?;
//! replace.delete(key.clone()).await?;
//! assert!(replace.load(key.clone()).await?.is_none());
//! replace.put(StateRecord::new(key.clone(), serde_json::json!({"status":"new"}))).await?;
//! replace.commit().await?;
//! assert_eq!(store.load(key).await?.unwrap().revision.get(), 3);
//! drop(store);
//! # Ok::<(), storage_kit::StateStoreError>(())
//! # })?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! A caller detecting a stale application precondition should roll back instead of
//! writing. Native serialization conflicts use [`crate::StateStoreError::TransactionConflict`];
//! a timeout or lost commit reply does not establish that the write was rolled back.
//!
//! # Directory-backed reopen
//!
//! Memory belongs to one connected owner and its clones. Directory connections
//! reopen persisted records. This example owns its temporary directory, consumes
//! all transactions and drops both store handles before removing that directory.
//! StateStore has no public shutdown operation; final-handle cleanup belongs to
//! the selected implementation.
//!
//! ```
//! use storage_kit::{StateStore, StateStoreBackend, StateTransactionBackend};
//! use storage_kit::state::config::{LanceStateStoreConfig, StateStoreConfig};
//! use storage_kit::state::record::{StateKey, StateRecord};
//! let directory = std::env::temp_dir().join(format!("ai-kit-state-doc-{}", uuid::Uuid::new_v4()));
//! std::fs::create_dir(&directory)?;
//! let result = tokio::runtime::Runtime::new()?.block_on(async {
//! let config = StateStoreConfig::lance(LanceStateStoreConfig::directory(&directory));
//! let store = StateStore::connect(config.clone()).await?;
//! let key = StateKey::new("settings", "theme")?;
//! let mut transaction = store.begin_transaction().await?;
//! transaction.put(StateRecord::new(key.clone(), serde_json::json!("dark"))).await?;
//! transaction.commit().await?;
//! drop(store);
//! let reopened = StateStore::connect(config).await?;
//! let observed = reopened.load(key).await?.expect("durable record");
//! assert_eq!(observed.record.value.as_ref(), &serde_json::json!("dark"));
//! assert_eq!(observed.revision.get(), 1);
//! drop(reopened);
//! Ok::<(), storage_kit::StateStoreError>(())
//! });
//! let cleanup = std::fs::remove_dir_all(&directory);
//! result?; // Preserve the operation error if cleanup also failed.
//! cleanup?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! Custom stores retain their own concrete associated transaction. The complete
//! [composition example](crate::StateStoreBackend) delegates native commit and
//! rollback instead of inventing transaction guarantees.
/// Built-in state-store construction data.
///
/// Lance is always available. Redb, PostgreSQL, and SQLite/Turso require
/// explicit `redb`, `postgres`, and `sqlite` features. Constructors and validation perform no connection I/O;
/// [`crate::StateStore::connect`] opens the selected store.
/// Revisioned records, keys, and pagination.
/// The unique owned transaction handle.
///
/// [`StateTransaction`](crate::state::transaction::StateTransaction) implements [`crate::StateTransactionBackend`]. Obtain
/// it from [`crate::StateStoreBackend::begin_transaction`]; it has no public
/// constructor. Caller-written stores return their own associated transaction
/// type instead of constructing this supplied handle.