use std::sync::Arc;
use dashmap::DashMap;
pub trait StorageEngine: Send + Sync + 'static {
fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
fn put(&self, key: &[u8], value: &[u8]) -> Result<usize, String>;
fn delete(&self, key: &[u8]) -> Result<(), String>;
fn scan_prefix(&self, prefix: &[u8]) -> Vec<(Vec<u8>, Vec<u8>)>;
fn flush(&self) -> Result<usize, String>;
}
#[derive(Debug, Default)]
pub struct MemoryEngine {
inner: DashMap<Vec<u8>, Vec<u8>>,
}
impl MemoryEngine {
pub fn new() -> Self {
Self::default()
}
}
impl StorageEngine for MemoryEngine {
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.inner.get(key).map(|v| v.clone())
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<usize, String> {
self.inner.insert(key.to_vec(), value.to_vec());
Ok(value.len())
}
fn delete(&self, key: &[u8]) -> Result<(), String> {
self.inner.remove(key);
Ok(())
}
fn scan_prefix(&self, prefix: &[u8]) -> Vec<(Vec<u8>, Vec<u8>)> {
self.inner
.iter()
.filter(|kv| kv.key().starts_with(prefix))
.map(|kv| (kv.key().clone(), kv.value().clone()))
.collect()
}
fn flush(&self) -> Result<usize, String> {
Ok(0)
}
}
pub type SharedEngine = Arc<dyn StorageEngine>;
#[cfg(feature = "sled")]
pub mod sled_engine {
use super::StorageEngine;
pub struct SledEngine {
tree: sled::Tree,
}
impl SledEngine {
pub fn new(tree: sled::Tree) -> Self {
Self { tree }
}
pub fn flush(&self) -> Result<usize, sled::Error> {
self.tree.flush()
}
pub fn cas(
&self,
key: Vec<u8>,
expected: Option<Vec<u8>>,
new_value: Vec<u8>,
) -> Result<Result<(), sled::CompareAndSwapError>, sled::Error> {
self.tree.compare_and_swap(key, expected, Some(new_value))
}
}
impl StorageEngine for SledEngine {
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.tree.get(key).ok().flatten().map(|v| v.to_vec())
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<usize, String> {
let len = value.len();
self.tree
.insert(key, value)
.map(|_| len)
.map_err(|e| format!("sled put: {e}"))
}
fn delete(&self, key: &[u8]) -> Result<(), String> {
self.tree
.remove(key)
.map(|_| ())
.map_err(|e| format!("sled delete: {e}"))
}
fn scan_prefix(&self, prefix: &[u8]) -> Vec<(Vec<u8>, Vec<u8>)> {
self.tree
.scan_prefix(prefix)
.filter_map(|r| match r {
Ok(kv) => Some((kv.0.to_vec(), kv.1.to_vec())),
Err(e) => {
tracing::warn!(error = %e, "sled scan_prefix entry error");
None
}
})
.collect()
}
fn flush(&self) -> Result<usize, String> {
self.tree.flush().map_err(|e| format!("sled flush: {e}"))
}
}
}
#[cfg(feature = "sled")]
pub use sled_engine::SledEngine;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn memory_engine_basic_ops() {
let e = MemoryEngine::new();
assert!(e.get(b"k").is_none());
e.put(b"k", b"v").unwrap();
assert_eq!(e.get(b"k"), Some(b"v".to_vec()));
e.delete(b"k").unwrap();
assert!(e.get(b"k").is_none());
}
#[test]
fn memory_engine_scan_prefix() {
let e = MemoryEngine::new();
e.put(b"a:1", b"x").unwrap();
e.put(b"a:2", b"y").unwrap();
e.put(b"b:1", b"z").unwrap();
let results = e.scan_prefix(b"a:");
assert_eq!(results.len(), 2);
}
}