pub trait StateBackend: Send + Sync {
// Required methods
fn get<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<Option<Vec<u8>>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn put<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
value: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn list_keys<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<Vec<Vec<u8>>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
// Provided methods
fn clear<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn count<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StateResult<usize>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn contains<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<bool>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
}Expand description
Core trait for state backend implementations
This trait defines the interface for storing and retrieving state in the stream processor. All operations are async to support both in-memory and persistent backends efficiently.
§Key Design Principles
- Byte-oriented: Keys and values are byte slices for maximum flexibility
- Async-first: All operations return futures for non-blocking I/O
- Simple interface: CRUD operations plus prefix listing
- Error handling: All operations return
StateResult<T>
§Implementation Requirements
Implementations must ensure:
- Thread safety: Concurrent access from multiple tasks
- Atomicity: Individual operations are atomic
- Durability (optional): Persistent backends should fsync writes
- Cleanup: Automatic cleanup of expired/deleted state
§Example Implementation
use async_trait::async_trait;
use processor::state::{StateBackend, StateResult};
use std::collections::HashMap;
use tokio::sync::RwLock;
struct SimpleBackend {
data: RwLock<HashMap<Vec<u8>, Vec<u8>>>,
}
#[async_trait]
impl StateBackend for SimpleBackend {
async fn get(&self, key: &[u8]) -> StateResult<Option<Vec<u8>>> {
Ok(self.data.read().await.get(key).cloned())
}
async fn put(&self, key: &[u8], value: &[u8]) -> StateResult<()> {
self.data.write().await.insert(key.to_vec(), value.to_vec());
Ok(())
}
async fn delete(&self, key: &[u8]) -> StateResult<()> {
self.data.write().await.remove(key);
Ok(())
}
async fn list_keys(&self, prefix: &[u8]) -> StateResult<Vec<Vec<u8>>> {
Ok(self.data.read().await
.keys()
.filter(|k| k.starts_with(prefix))
.cloned()
.collect())
}
}Required Methods§
Sourcefn get<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<Option<Vec<u8>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<Option<Vec<u8>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Retrieve a value for the given key
Returns Ok(None) if the key does not exist or has expired.
§Arguments
key- The key to look up (as byte slice)
§Returns
Ok(Some(value))- Value foundOk(None)- Key not found or expiredErr(StateError)- Backend error occurred
§Example
let backend = MemoryStateBackend::new(None);
backend.put(b"key1", b"value1").await?;
match backend.get(b"key1").await? {
Some(value) => println!("Found: {:?}", value),
None => println!("Not found"),
}Sourcefn put<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
value: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn put<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
value: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Store a value for the given key
If the key already exists, its value is overwritten.
§Arguments
key- The key to store (as byte slice)value- The value to store (as byte slice)
§Returns
Ok(())- Value stored successfullyErr(StateError)- Backend error occurred
§Example
let backend = MemoryStateBackend::new(None);
backend.put(b"window:123", b"aggregated_state").await?;Sourcefn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Delete a key and its associated value
This operation is idempotent - deleting a non-existent key is not an error.
§Arguments
key- The key to delete (as byte slice)
§Returns
Ok(())- Key deleted or didn’t existErr(StateError)- Backend error occurred
§Example
let backend = MemoryStateBackend::new(None);
backend.put(b"temp:123", b"data").await?;
backend.delete(b"temp:123").await?;Sourcefn list_keys<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<Vec<Vec<u8>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn list_keys<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 [u8],
) -> Pin<Box<dyn Future<Output = StateResult<Vec<Vec<u8>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
List all keys with the given prefix
This is useful for finding all windows for a specific key, or all state entries of a certain type.
§Arguments
prefix- The prefix to match (as byte slice). Empty prefix matches all keys.
§Returns
Ok(Vec<Vec<u8>>)- List of matching keysErr(StateError)- Backend error occurred
§Example
let backend = MemoryStateBackend::new(None);
backend.put(b"window:1", b"data1").await?;
backend.put(b"window:2", b"data2").await?;
backend.put(b"meta:1", b"metadata").await?;
// List all window keys
let window_keys = backend.list_keys(b"window:").await?;
assert_eq!(window_keys.len(), 2);Provided Methods§
Sourcefn clear<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn clear<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StateResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Clear all state from the backend
This is primarily used for testing and cleanup. Use with caution.
§Returns
Ok(())- All state clearedErr(StateError)- Backend error occurred
Sourcefn count<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StateResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn count<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StateResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Get the number of keys in the backend
This is an optional operation that may be expensive for some backends.
§Returns
Ok(count)- Number of keysErr(StateError)- Backend error occurred
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".