llm_optimizer_processor/state/backend.rs
1//! State backend trait definition
2//!
3//! This module defines the core `StateBackend` trait that all state storage
4//! implementations must implement. The trait provides a simple key-value
5//! interface with support for listing keys by prefix.
6
7use async_trait::async_trait;
8
9use crate::error::StateResult;
10
11/// Core trait for state backend implementations
12///
13/// This trait defines the interface for storing and retrieving state in the
14/// stream processor. All operations are async to support both in-memory and
15/// persistent backends efficiently.
16///
17/// ## Key Design Principles
18///
19/// - **Byte-oriented**: Keys and values are byte slices for maximum flexibility
20/// - **Async-first**: All operations return futures for non-blocking I/O
21/// - **Simple interface**: CRUD operations plus prefix listing
22/// - **Error handling**: All operations return `StateResult<T>`
23///
24/// ## Implementation Requirements
25///
26/// Implementations must ensure:
27///
28/// - **Thread safety**: Concurrent access from multiple tasks
29/// - **Atomicity**: Individual operations are atomic
30/// - **Durability** (optional): Persistent backends should fsync writes
31/// - **Cleanup**: Automatic cleanup of expired/deleted state
32///
33/// ## Example Implementation
34///
35/// ```rust,no_run
36/// use async_trait::async_trait;
37/// use processor::state::{StateBackend, StateResult};
38/// use std::collections::HashMap;
39/// use tokio::sync::RwLock;
40///
41/// struct SimpleBackend {
42/// data: RwLock<HashMap<Vec<u8>, Vec<u8>>>,
43/// }
44///
45/// #[async_trait]
46/// impl StateBackend for SimpleBackend {
47/// async fn get(&self, key: &[u8]) -> StateResult<Option<Vec<u8>>> {
48/// Ok(self.data.read().await.get(key).cloned())
49/// }
50///
51/// async fn put(&self, key: &[u8], value: &[u8]) -> StateResult<()> {
52/// self.data.write().await.insert(key.to_vec(), value.to_vec());
53/// Ok(())
54/// }
55///
56/// async fn delete(&self, key: &[u8]) -> StateResult<()> {
57/// self.data.write().await.remove(key);
58/// Ok(())
59/// }
60///
61/// async fn list_keys(&self, prefix: &[u8]) -> StateResult<Vec<Vec<u8>>> {
62/// Ok(self.data.read().await
63/// .keys()
64/// .filter(|k| k.starts_with(prefix))
65/// .cloned()
66/// .collect())
67/// }
68/// }
69/// ```
70#[async_trait]
71pub trait StateBackend: Send + Sync {
72 /// Retrieve a value for the given key
73 ///
74 /// Returns `Ok(None)` if the key does not exist or has expired.
75 ///
76 /// # Arguments
77 ///
78 /// * `key` - The key to look up (as byte slice)
79 ///
80 /// # Returns
81 ///
82 /// * `Ok(Some(value))` - Value found
83 /// * `Ok(None)` - Key not found or expired
84 /// * `Err(StateError)` - Backend error occurred
85 ///
86 /// # Example
87 ///
88 /// ```rust,no_run
89 /// # use processor::state::{StateBackend, MemoryStateBackend};
90 /// # #[tokio::main]
91 /// # async fn main() -> anyhow::Result<()> {
92 /// let backend = MemoryStateBackend::new(None);
93 /// backend.put(b"key1", b"value1").await?;
94 ///
95 /// match backend.get(b"key1").await? {
96 /// Some(value) => println!("Found: {:?}", value),
97 /// None => println!("Not found"),
98 /// }
99 /// # Ok(())
100 /// # }
101 /// ```
102 async fn get(&self, key: &[u8]) -> StateResult<Option<Vec<u8>>>;
103
104 /// Store a value for the given key
105 ///
106 /// If the key already exists, its value is overwritten.
107 ///
108 /// # Arguments
109 ///
110 /// * `key` - The key to store (as byte slice)
111 /// * `value` - The value to store (as byte slice)
112 ///
113 /// # Returns
114 ///
115 /// * `Ok(())` - Value stored successfully
116 /// * `Err(StateError)` - Backend error occurred
117 ///
118 /// # Example
119 ///
120 /// ```rust,no_run
121 /// # use processor::state::{StateBackend, MemoryStateBackend};
122 /// # #[tokio::main]
123 /// # async fn main() -> anyhow::Result<()> {
124 /// let backend = MemoryStateBackend::new(None);
125 /// backend.put(b"window:123", b"aggregated_state").await?;
126 /// # Ok(())
127 /// # }
128 /// ```
129 async fn put(&self, key: &[u8], value: &[u8]) -> StateResult<()>;
130
131 /// Delete a key and its associated value
132 ///
133 /// This operation is idempotent - deleting a non-existent key is not an error.
134 ///
135 /// # Arguments
136 ///
137 /// * `key` - The key to delete (as byte slice)
138 ///
139 /// # Returns
140 ///
141 /// * `Ok(())` - Key deleted or didn't exist
142 /// * `Err(StateError)` - Backend error occurred
143 ///
144 /// # Example
145 ///
146 /// ```rust,no_run
147 /// # use processor::state::{StateBackend, MemoryStateBackend};
148 /// # #[tokio::main]
149 /// # async fn main() -> anyhow::Result<()> {
150 /// let backend = MemoryStateBackend::new(None);
151 /// backend.put(b"temp:123", b"data").await?;
152 /// backend.delete(b"temp:123").await?;
153 /// # Ok(())
154 /// # }
155 /// ```
156 async fn delete(&self, key: &[u8]) -> StateResult<()>;
157
158 /// List all keys with the given prefix
159 ///
160 /// This is useful for finding all windows for a specific key, or all state
161 /// entries of a certain type.
162 ///
163 /// # Arguments
164 ///
165 /// * `prefix` - The prefix to match (as byte slice). Empty prefix matches all keys.
166 ///
167 /// # Returns
168 ///
169 /// * `Ok(Vec<Vec<u8>>)` - List of matching keys
170 /// * `Err(StateError)` - Backend error occurred
171 ///
172 /// # Example
173 ///
174 /// ```rust,no_run
175 /// # use processor::state::{StateBackend, MemoryStateBackend};
176 /// # #[tokio::main]
177 /// # async fn main() -> anyhow::Result<()> {
178 /// let backend = MemoryStateBackend::new(None);
179 /// backend.put(b"window:1", b"data1").await?;
180 /// backend.put(b"window:2", b"data2").await?;
181 /// backend.put(b"meta:1", b"metadata").await?;
182 ///
183 /// // List all window keys
184 /// let window_keys = backend.list_keys(b"window:").await?;
185 /// assert_eq!(window_keys.len(), 2);
186 /// # Ok(())
187 /// # }
188 /// ```
189 async fn list_keys(&self, prefix: &[u8]) -> StateResult<Vec<Vec<u8>>>;
190
191 /// Clear all state from the backend
192 ///
193 /// This is primarily used for testing and cleanup. Use with caution.
194 ///
195 /// # Returns
196 ///
197 /// * `Ok(())` - All state cleared
198 /// * `Err(StateError)` - Backend error occurred
199 async fn clear(&self) -> StateResult<()> {
200 let keys = self.list_keys(b"").await?;
201 for key in keys {
202 self.delete(&key).await?;
203 }
204 Ok(())
205 }
206
207 /// Get the number of keys in the backend
208 ///
209 /// This is an optional operation that may be expensive for some backends.
210 ///
211 /// # Returns
212 ///
213 /// * `Ok(count)` - Number of keys
214 /// * `Err(StateError)` - Backend error occurred
215 async fn count(&self) -> StateResult<usize> {
216 Ok(self.list_keys(b"").await?.len())
217 }
218
219 /// Check if a key exists
220 ///
221 /// This is more efficient than `get()` when you only need to check existence.
222 ///
223 /// # Arguments
224 ///
225 /// * `key` - The key to check (as byte slice)
226 ///
227 /// # Returns
228 ///
229 /// * `Ok(true)` - Key exists
230 /// * `Ok(false)` - Key does not exist
231 /// * `Err(StateError)` - Backend error occurred
232 async fn contains(&self, key: &[u8]) -> StateResult<bool> {
233 Ok(self.get(key).await?.is_some())
234 }
235}
236
237#[cfg(test)]
238pub mod tests {
239 use super::*;
240
241 // Helper to create a test key
242 fn test_key(id: u32) -> Vec<u8> {
243 format!("test:{}", id).into_bytes()
244 }
245
246 // Generic tests that any StateBackend implementation should pass
247 pub async fn test_backend_basic_ops<B: StateBackend>(backend: B) {
248 // Test put and get
249 backend.put(&test_key(1), b"value1").await.unwrap();
250 let value = backend.get(&test_key(1)).await.unwrap();
251 assert_eq!(value, Some(b"value1".to_vec()));
252
253 // Test get non-existent key
254 let value = backend.get(&test_key(999)).await.unwrap();
255 assert_eq!(value, None);
256
257 // Test overwrite
258 backend.put(&test_key(1), b"new_value").await.unwrap();
259 let value = backend.get(&test_key(1)).await.unwrap();
260 assert_eq!(value, Some(b"new_value".to_vec()));
261
262 // Test delete
263 backend.delete(&test_key(1)).await.unwrap();
264 let value = backend.get(&test_key(1)).await.unwrap();
265 assert_eq!(value, None);
266
267 // Test delete non-existent (should not error)
268 backend.delete(&test_key(999)).await.unwrap();
269 }
270
271 pub async fn test_backend_list_keys<B: StateBackend>(backend: B) {
272 // Clear any existing data
273 backend.clear().await.unwrap();
274
275 // Add some keys with different prefixes
276 backend.put(b"window:1", b"w1").await.unwrap();
277 backend.put(b"window:2", b"w2").await.unwrap();
278 backend.put(b"window:3", b"w3").await.unwrap();
279 backend.put(b"meta:1", b"m1").await.unwrap();
280 backend.put(b"meta:2", b"m2").await.unwrap();
281
282 // Test prefix matching
283 let window_keys = backend.list_keys(b"window:").await.unwrap();
284 assert_eq!(window_keys.len(), 3);
285
286 let meta_keys = backend.list_keys(b"meta:").await.unwrap();
287 assert_eq!(meta_keys.len(), 2);
288
289 // Test empty prefix (all keys)
290 let all_keys = backend.list_keys(b"").await.unwrap();
291 assert_eq!(all_keys.len(), 5);
292 }
293
294 pub async fn test_backend_clear<B: StateBackend>(backend: B) {
295 backend.put(b"key1", b"val1").await.unwrap();
296 backend.put(b"key2", b"val2").await.unwrap();
297 backend.put(b"key3", b"val3").await.unwrap();
298
299 assert_eq!(backend.count().await.unwrap(), 3);
300
301 backend.clear().await.unwrap();
302
303 assert_eq!(backend.count().await.unwrap(), 0);
304 }
305
306 pub async fn test_backend_contains<B: StateBackend>(backend: B) {
307 backend.clear().await.unwrap();
308
309 assert!(!backend.contains(b"test").await.unwrap());
310
311 backend.put(b"test", b"value").await.unwrap();
312
313 assert!(backend.contains(b"test").await.unwrap());
314
315 backend.delete(b"test").await.unwrap();
316
317 assert!(!backend.contains(b"test").await.unwrap());
318 }
319}