Skip to content

Commit ad890a0

Browse files
committed
Add storage abstraction layer for metadata persistence
1 parent 09cc44e commit ad890a0

6 files changed

Lines changed: 139 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ categories = ["database", "network-programming"]
1515

1616
[workspace.dependencies]
1717
thiserror = "2"
18+
tracing = "0.1"
19+
1820
base64 = "0.22"
1921
blake3 = "1.8"
2022
dashmap = "6.1"

minikv-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ categories.workspace = true
1212

1313
[dependencies]
1414
thiserror.workspace = true
15+
tracing.workspace = true
1516
base64 = { workspace = true }
1617
blake3 = { workspace = true }
1718
dashmap = { workspace = true }

minikv-core/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use thiserror::Error;
2+
3+
/// Unified error type for minikv.
4+
#[derive(Debug, Error)]
5+
pub enum Error {}

minikv-core/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
pub mod error;
12
pub mod hashing;
23
pub mod locking;
4+
pub mod storage;
35
pub mod volumes;
6+
7+
pub use error::Error;

minikv-core/src/storage/mod.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! Storage abstraction layer for metadata persistence.
2+
//!
3+
//! All interactions with LevelDB or in-memory stores go through the
4+
//! `MetadataStore` trait. This isolates storage concerns from business logic
5+
//! and allows swapping backends in tests or future implementations.
6+
7+
use crate::error::Error;
8+
9+
/// A single key-value pair in a metadata store.
10+
///
11+
/// - The first element (`Vec<u8>`) is the key.
12+
/// - The second element (`Vec<u8>`) is the associated value.
13+
///
14+
/// This type is used throughout `MetadataStore` APIs for operations that
15+
/// return or manipulate multiple entries, such as `scan_prefix` or `scan_all`.
16+
pub type KeyValuePair = (Vec<u8>, Vec<u8>);
17+
18+
/// Trait defining the interface for all metadata stores.
19+
///
20+
/// Implementors must be `Send + Sync` to allow sharing via `Arc<dyn MetadataStore>`
21+
/// across async tasks.
22+
pub trait MetadataStore: Send + Sync {
23+
/// Retrieve the value associated with `key`. Returns `None` if the key is absent.
24+
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>;
25+
26+
/// Insert or update a key-value pair.
27+
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error>;
28+
29+
/// Delete the given key. Succeeds silently if the key does not exist.
30+
fn delete(&self, key: &[u8]) -> Result<(), Error>;
31+
32+
/// Return all key-value pairs with keys starting with `prefix`,
33+
/// ordered lexicographically by key.
34+
fn scan_prefix(&self, prefix: &[u8]) -> Result<Vec<KeyValuePair>, Error>;
35+
36+
/// Return all key-value pairs in the store.
37+
fn scan_all(&self) -> Result<Vec<KeyValuePair>, Error>;
38+
39+
/// Remove all entries in the store.
40+
/// Primarily used by `rebuild_all` to regenerate the database from scratch.
41+
fn delete_all(&self) -> Result<(), Error>;
42+
}
43+
44+
/// In-memory `MetadataStore` backed by a `BTreeMap`.
45+
///
46+
/// Useful for unit and integration tests that do not require persistent storage.
47+
#[cfg(debug_assertions)]
48+
pub mod mem {
49+
use super::*;
50+
use std::collections::BTreeMap;
51+
use std::sync::Mutex;
52+
53+
#[derive(Default)]
54+
pub struct MemStore {
55+
inner: Mutex<BTreeMap<Vec<u8>, Vec<u8>>>,
56+
}
57+
58+
impl MetadataStore for MemStore {
59+
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
60+
Ok(self.inner.lock().unwrap().get(key).cloned())
61+
}
62+
63+
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
64+
self.inner
65+
.lock()
66+
.unwrap()
67+
.insert(key.to_vec(), value.to_vec());
68+
Ok(())
69+
}
70+
71+
fn delete(&self, key: &[u8]) -> Result<(), Error> {
72+
self.inner.lock().unwrap().remove(key);
73+
Ok(())
74+
}
75+
76+
fn scan_prefix(&self, prefix: &[u8]) -> Result<Vec<KeyValuePair>, Error> {
77+
let store = self.inner.lock().unwrap();
78+
Ok(store
79+
.iter()
80+
.filter(|(k, _)| k.starts_with(prefix))
81+
.map(|(k, v)| (k.clone(), v.clone()))
82+
.collect())
83+
}
84+
85+
fn scan_all(&self) -> Result<Vec<KeyValuePair>, Error> {
86+
let store = self.inner.lock().unwrap();
87+
Ok(store.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
88+
}
89+
90+
fn delete_all(&self) -> Result<(), Error> {
91+
self.inner.lock().unwrap().clear();
92+
Ok(())
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)