ethrex_storage/backend/
in_memory.rs1use crate::api::{
2 PrefixResult, StorageBackend, StorageLockedView, StorageReadView, StorageWriteBatch,
3};
4use crate::error::StoreError;
5use rustc_hash::FxHashMap;
6use std::path::Path;
7use std::sync::{Arc, RwLock};
8
9type Table = FxHashMap<Vec<u8>, Vec<u8>>;
10type Database = FxHashMap<&'static str, Table>;
11
12#[derive(Debug)]
13pub struct InMemoryBackend {
14 inner: Arc<RwLock<Arc<Database>>>,
18}
19
20impl InMemoryBackend {
21 pub fn open() -> Result<Self, StoreError> {
22 Ok(Self {
23 inner: Arc::new(RwLock::new(Arc::new(Database::default()))),
24 })
25 }
26}
27
28impl StorageBackend for InMemoryBackend {
29 fn clear_table(&self, table: &str) -> Result<(), StoreError> {
30 let mut db = self
31 .inner
32 .write()
33 .map_err(|_| StoreError::Custom("Failed to acquire write lock".to_string()))?;
34
35 let db_mut = Arc::make_mut(&mut *db);
36 if let Some(table_ref) = db_mut.get_mut(table) {
37 table_ref.clear();
38 }
39 Ok(())
40 }
41
42 fn begin_read(&self) -> Result<Arc<dyn StorageReadView>, StoreError> {
43 let snapshot = self
44 .inner
45 .read()
46 .map_err(|_| StoreError::Custom("Failed to acquire read lock".to_string()))?
47 .clone();
48 Ok(Arc::new(InMemoryReadTx { snapshot }))
49 }
50
51 fn begin_write(&self) -> Result<Box<dyn StorageWriteBatch + 'static>, StoreError> {
52 Ok(Box::new(InMemoryWriteTx {
53 backend: self.inner.clone(),
54 }))
55 }
56
57 fn begin_locked(
58 &self,
59 table_name: &'static str,
60 ) -> Result<Box<dyn StorageLockedView>, StoreError> {
61 let snapshot = self
62 .inner
63 .read()
64 .map_err(|_| StoreError::Custom("Failed to acquire read lock".to_string()))?
65 .clone();
66 Ok(Box::new(InMemoryLocked {
67 snapshot,
68 table_name,
69 }))
70 }
71
72 fn create_checkpoint(&self, _path: &Path) -> Result<(), StoreError> {
73 Ok(())
76 }
77}
78
79pub struct InMemoryLocked {
80 snapshot: Arc<Database>,
81 table_name: &'static str,
82}
83
84pub struct InMemoryPrefixIter {
85 results: std::vec::IntoIter<PrefixResult>,
86}
87
88impl Iterator for InMemoryPrefixIter {
89 type Item = PrefixResult;
90
91 fn next(&mut self) -> Option<Self::Item> {
92 self.results.next()
93 }
94}
95
96impl StorageLockedView for InMemoryLocked {
97 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StoreError> {
98 Ok(self
99 .snapshot
100 .get(&self.table_name)
101 .and_then(|table_ref| table_ref.get(key))
102 .cloned())
103 }
104}
105
106pub struct InMemoryReadTx {
107 snapshot: Arc<Database>,
108}
109
110impl StorageReadView for InMemoryReadTx {
111 fn get(&self, table: &str, key: &[u8]) -> Result<Option<Vec<u8>>, StoreError> {
112 Ok(self
113 .snapshot
114 .get(table)
115 .and_then(|table_ref| table_ref.get(key))
116 .cloned())
117 }
118
119 fn prefix_iterator(
120 &self,
121 table: &str,
122 prefix: &[u8],
123 ) -> Result<Box<dyn Iterator<Item = PrefixResult> + '_>, StoreError> {
124 let table_data = self.snapshot.get(table).cloned().unwrap_or_default();
125 let prefix_vec = prefix.to_vec();
126
127 let mut entries: Vec<(Vec<u8>, Vec<u8>)> = table_data
128 .into_iter()
129 .filter(|(key, _)| key.starts_with(&prefix_vec))
130 .collect();
131 entries.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
132
133 let results: Vec<PrefixResult> = entries
134 .into_iter()
135 .map(|(k, v)| Ok((k.into_boxed_slice(), v.into_boxed_slice())))
136 .collect();
137
138 let iter = InMemoryPrefixIter {
139 results: results.into_iter(),
140 };
141 Ok(Box::new(iter))
142 }
143
144 fn first_key(&self, table: &'static str) -> Result<Option<Vec<u8>>, StoreError> {
145 let Some(table_data) = self.snapshot.get(table) else {
146 return Ok(None);
147 };
148 Ok(table_data.keys().min().cloned())
149 }
150
151 fn last_key(&self, table: &'static str) -> Result<Option<Vec<u8>>, StoreError> {
152 let Some(table_data) = self.snapshot.get(table) else {
153 return Ok(None);
154 };
155 Ok(table_data.keys().max().cloned())
156 }
157}
158
159pub struct InMemoryWriteTx {
160 backend: Arc<RwLock<Arc<Database>>>,
161}
162
163impl StorageWriteBatch for InMemoryWriteTx {
164 fn put_batch(
165 &mut self,
166 table: &'static str,
167 batch: Vec<(Vec<u8>, Vec<u8>)>,
168 ) -> Result<(), StoreError> {
169 let mut db = self
170 .backend
171 .write()
172 .map_err(|_| StoreError::Custom("Failed to acquire write lock".to_string()))?;
173
174 let db_mut = Arc::make_mut(&mut *db);
176 let table_ref = db_mut.entry(table).or_default();
177
178 for (key, value) in batch {
179 table_ref.insert(key, value);
180 }
181
182 Ok(())
183 }
184
185 fn delete(&mut self, table: &str, key: &[u8]) -> Result<(), StoreError> {
186 let mut db = self
187 .backend
188 .write()
189 .map_err(|_| StoreError::Custom("Failed to acquire write lock".to_string()))?;
190
191 let db_mut = Arc::make_mut(&mut *db);
192 if let Some(table_ref) = db_mut.get_mut(table) {
193 table_ref.remove(key);
194 }
195 Ok(())
196 }
197
198 fn delete_range(
199 &mut self,
200 table: &'static str,
201 start: &[u8],
202 end: &[u8],
203 ) -> Result<(), StoreError> {
204 let mut db = self
205 .backend
206 .write()
207 .map_err(|_| StoreError::Custom("Failed to acquire write lock".to_string()))?;
208
209 let db_mut = Arc::make_mut(&mut *db);
210 if let Some(table_ref) = db_mut.get_mut(table) {
211 table_ref.retain(|k, _| !(k.as_slice() >= start && k.as_slice() < end));
212 }
213 Ok(())
214 }
215
216 fn merge(&mut self, table: &'static str, key: &[u8], operand: &[u8]) -> Result<(), StoreError> {
217 if table != crate::api::tables::TRANSACTION_LOCATIONS {
220 return Err(StoreError::Custom(format!(
221 "merge not supported for table {table}"
222 )));
223 }
224 let mut db = self
225 .backend
226 .write()
227 .map_err(|_| StoreError::Custom("Failed to acquire write lock".to_string()))?;
228 let db_mut = Arc::make_mut(&mut *db);
229 let table_ref = db_mut.entry(table).or_default();
230 let existing = table_ref.get(key).map(|v| v.as_slice());
231 let merged = crate::store::tx_locations_merge(existing, std::iter::once(operand))
232 .ok_or_else(|| StoreError::Custom("tx_locations_merge returned None".to_string()))?;
233 table_ref.insert(key.to_vec(), merged);
234 Ok(())
235 }
236
237 fn commit(&mut self) -> Result<(), StoreError> {
238 Ok(())
247 }
248}