use crate::operations::{KVOperation, KVResult, StoreError};
use crate::store::{KVStore, KVStoreConfig, ValueEntry};
use async_trait::async_trait;
use rabia_core::smr::StateMachine;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct KVStoreState {
pub data: HashMap<String, ValueEntry>,
pub version: u64,
}
pub struct KVStoreSMR {
store: KVStore,
}
impl Clone for KVStoreSMR {
fn clone(&self) -> Self {
let config = self.store.config.clone();
let new_store = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async { KVStore::new(config).await })
})
.expect("Failed to create new KVStore instance");
let current_data = self.store.get_all_data();
let current_version = self.store.current_version();
new_store.set_all_data(current_data);
new_store.set_version(current_version);
Self { store: new_store }
}
}
impl KVStoreSMR {
pub async fn new(config: KVStoreConfig) -> Result<Self, StoreError> {
let store = KVStore::new(config).await?;
Ok(Self { store })
}
pub async fn new_default() -> Result<Self, StoreError> {
Self::new(KVStoreConfig::default()).await
}
pub fn store(&self) -> &KVStore {
&self.store
}
}
#[async_trait]
impl StateMachine for KVStoreSMR {
type Command = KVOperation;
type Response = KVResult;
type State = KVStoreState;
async fn apply_command(&mut self, command: Self::Command) -> Self::Response {
match command {
KVOperation::Set { key, value } => match self.store.set(&key, &value).await {
Ok(result) => result,
Err(e) => KVResult::Error(e.to_string()),
},
KVOperation::Get { key } => match self.store.get(&key).await {
Ok(Some(_)) => KVResult::Success,
Ok(None) => KVResult::NotFound,
Err(e) => KVResult::Error(e.to_string()),
},
KVOperation::Delete { key } => match self.store.delete(&key).await {
Ok(result) => result,
Err(e) => KVResult::Error(e.to_string()),
},
KVOperation::Exists { key } => match self.store.exists(&key).await {
Ok(true) => KVResult::Success,
Ok(false) => KVResult::NotFound,
Err(e) => KVResult::Error(e.to_string()),
},
}
}
fn get_state(&self) -> Self::State {
let data = self.store.get_all_data();
let version = self.store.current_version();
KVStoreState { data, version }
}
fn set_state(&mut self, state: Self::State) {
self.store.set_all_data(state.data);
self.store.set_version(state.version);
}
fn serialize_state(&self) -> Vec<u8> {
let state = self.get_state();
bincode::serialize(&state).unwrap_or_default()
}
fn deserialize_state(&mut self, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let state: KVStoreState = bincode::deserialize(data)?;
self.set_state(state);
Ok(())
}
async fn apply_commands(&mut self, commands: Vec<Self::Command>) -> Vec<Self::Response> {
let mut responses = Vec::with_capacity(commands.len());
for command in commands {
responses.push(self.apply_command(command).await);
}
responses
}
fn is_deterministic(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_kvstore_smr_basic_operations() {
let mut smr = KVStoreSMR::new_default().await.unwrap();
let set_cmd = KVOperation::Set {
key: "test_key".to_string(),
value: "test_value".to_string(),
};
let result = smr.apply_command(set_cmd).await;
assert!(result.is_success());
let get_cmd = KVOperation::Get {
key: "test_key".to_string(),
};
let result = smr.apply_command(get_cmd).await;
assert!(result.is_success());
let exists_cmd = KVOperation::Exists {
key: "test_key".to_string(),
};
let result = smr.apply_command(exists_cmd).await;
assert!(result.is_success());
let delete_cmd = KVOperation::Delete {
key: "test_key".to_string(),
};
let result = smr.apply_command(delete_cmd).await;
assert!(result.is_success());
let get_cmd = KVOperation::Get {
key: "test_key".to_string(),
};
let result = smr.apply_command(get_cmd).await;
assert!(result.is_not_found());
}
#[tokio::test]
async fn test_kvstore_smr_state_serialization() {
let mut smr = KVStoreSMR::new_default().await.unwrap();
let set_cmd1 = KVOperation::Set {
key: "key1".to_string(),
value: "value1".to_string(),
};
let set_cmd2 = KVOperation::Set {
key: "key2".to_string(),
value: "value2".to_string(),
};
smr.apply_command(set_cmd1).await;
smr.apply_command(set_cmd2).await;
let serialized = smr.serialize_state();
assert!(!serialized.is_empty());
let mut new_smr = KVStoreSMR::new_default().await.unwrap();
new_smr.deserialize_state(&serialized).unwrap();
let state = new_smr.get_state();
assert_eq!(state.data.len(), 2);
assert!(state.data.contains_key("key1"));
assert!(state.data.contains_key("key2"));
assert_eq!(state.data["key1"].value, "value1");
assert_eq!(state.data["key2"].value, "value2");
}
#[tokio::test]
async fn test_kvstore_smr_multiple_commands() {
let mut smr = KVStoreSMR::new_default().await.unwrap();
let commands = vec![
KVOperation::Set {
key: "key1".to_string(),
value: "value1".to_string(),
},
KVOperation::Set {
key: "key2".to_string(),
value: "value2".to_string(),
},
KVOperation::Get {
key: "key1".to_string(),
},
KVOperation::Delete {
key: "key2".to_string(),
},
KVOperation::Get {
key: "key2".to_string(),
},
];
let responses = smr.apply_commands(commands).await;
assert_eq!(responses.len(), 5);
assert!(responses[0].is_success()); assert!(responses[1].is_success()); assert!(responses[2].is_success()); assert!(responses[3].is_success()); assert!(responses[4].is_not_found()); }
}