-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathmod.rs
More file actions
146 lines (124 loc) · 4.58 KB
/
Copy pathmod.rs
File metadata and controls
146 lines (124 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
pub mod config;
pub mod factory;
pub mod in_memory;
pub mod loader;
pub mod slate;
use std::sync::Arc;
use async_trait::async_trait;
use bytes::Bytes;
use crate::BytesRange;
#[derive(Clone, Debug)]
pub struct Record {
pub key: Bytes,
pub value: Bytes,
}
impl Record {
pub fn new(key: Bytes, value: Bytes) -> Self {
Self { key, value }
}
pub fn empty(key: Bytes) -> Self {
Self::new(key, Bytes::new())
}
}
pub enum RecordOp {
Put(Record),
Merge(Record),
}
/// Error type for storage operations
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StorageError {
/// Storage-related errors
Storage(String),
/// Internal errors
Internal(String),
}
impl std::error::Error for StorageError {}
impl std::fmt::Display for StorageError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
StorageError::Storage(msg) => write!(f, "Storage error: {}", msg),
StorageError::Internal(msg) => write!(f, "Internal error: {}", msg),
}
}
}
impl StorageError {
/// Converts a storage error to StorageError::Storage.
pub fn from_storage(e: impl std::fmt::Display) -> Self {
StorageError::Storage(e.to_string())
}
}
/// Result type alias for storage operations
pub type StorageResult<T> = std::result::Result<T, StorageError>;
/// Trait for merging existing values with new values.
///
/// Merge operators must be associative: `merge(merge(a, b), c) == merge(a, merge(b, c))`.
/// This ensures consistent merging behavior regardless of the order of operations.
pub trait MergeOperator: Send + Sync {
/// Merges an existing value with a new value to produce a merged result.
///
/// # Arguments
/// * `key` - The key associated with the values being merged
/// * `existing_value` - The current value stored in the database (if any)
/// * `new_value` - The new value to merge with the existing value
///
/// # Returns
/// The merged value.
fn merge(&self, key: &Bytes, existing_value: Option<Bytes>, new_value: Bytes) -> Bytes;
}
/// Iterator over storage records.
#[async_trait]
pub trait StorageIterator {
async fn next(&mut self) -> StorageResult<Option<Record>>;
}
/// Common read operations supported by both Storage and StorageSnapshot.
///
/// This trait provides the core read methods that are shared between full storage
/// access and point-in-time snapshots. By extracting these common operations,
/// we can write code that works with both storage types.
#[async_trait]
pub trait StorageRead: Send + Sync {
async fn get(&self, key: Bytes) -> StorageResult<Option<Record>>;
/// Returns an iterator over records in the given range.
async fn scan_iter(
&self,
range: BytesRange,
) -> StorageResult<Box<dyn StorageIterator + Send + '_>>;
/// Collects all records in the range into a Vec.
#[tracing::instrument(level = "trace", skip_all)]
async fn scan(&self, range: BytesRange) -> StorageResult<Vec<Record>> {
let mut iter = self.scan_iter(range).await?;
let mut records = Vec::new();
while let Some(record) = iter.next().await? {
records.push(record);
}
Ok(records)
}
}
/// A point-in-time snapshot of the storage layer.
///
/// Snapshots provide a consistent read-only view of the database at the time
/// the snapshot was created. Reads from a snapshot will not see any subsequent
/// writes to the underlying storage.
#[async_trait]
pub trait StorageSnapshot: StorageRead {}
/// The storage type encapsulates access to the underlying storage (e.g. SlateDB).
#[async_trait]
pub trait Storage: StorageRead {
async fn apply(&self, ops: Vec<RecordOp>) -> StorageResult<()>;
async fn put(&self, records: Vec<Record>) -> StorageResult<()>;
/// Merges values for the given keys using the configured merge operator.
///
/// This method requires the underlying storage engine to be configured with
/// a merge operator. If no merge operator is configured, this method will
/// return a `StorageError::Storage` error.
///
/// The merge operation is atomic - all merges in the batch are applied
/// together or not at all.
async fn merge(&self, records: Vec<Record>) -> StorageResult<()>;
/// Creates a point-in-time snapshot of the storage.
///
/// The snapshot provides a consistent read-only view of the database at the time
/// the snapshot was created. Reads from the snapshot will not see any subsequent
/// writes to the underlying storage.
async fn snapshot(&self) -> StorageResult<Arc<dyn StorageSnapshot>>;
}