1use std::{collections::HashMap, ops::Bound};
10
11use reifydb_core::common::CommitVersion;
12use reifydb_type::{Result, util::cowvec::CowVec};
13
14use super::memory::storage::MemoryPrimitiveStorage;
15#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
16use super::sqlite::config::SqliteConfig;
17#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
18use super::sqlite::storage::SqlitePrimitiveStorage;
19use crate::tier::{EntryKind, RangeBatch, RangeCursor, TierBackend, TierStorage};
20
21#[derive(Clone)]
26#[repr(u8)]
27pub enum HotStorage {
28 Memory(MemoryPrimitiveStorage) = 0,
30 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
32 Sqlite(SqlitePrimitiveStorage) = 1,
33}
34
35impl HotStorage {
36 pub fn memory() -> Self {
38 Self::Memory(MemoryPrimitiveStorage::new())
39 }
40
41 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
43 pub fn sqlite_in_memory() -> Self {
44 Self::Sqlite(SqlitePrimitiveStorage::in_memory())
45 }
46
47 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
49 pub fn sqlite(config: SqliteConfig) -> Self {
50 Self::Sqlite(SqlitePrimitiveStorage::new(config))
51 }
52}
53
54impl HotStorage {
55 pub fn maintenance(&self) {
57 match self {
58 Self::Memory(_) => {}
59 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
60 Self::Sqlite(s) => {
61 s.incremental_vacuum();
62 s.shrink_memory();
63 }
64 }
65 }
66}
67
68impl TierStorage for HotStorage {
69 #[inline]
70 fn get(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<Option<CowVec<u8>>> {
71 match self {
72 Self::Memory(s) => s.get(table, key, version),
73 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
74 Self::Sqlite(s) => s.get(table, key, version),
75 }
76 }
77
78 #[inline]
79 fn contains(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<bool> {
80 match self {
81 Self::Memory(s) => s.contains(table, key, version),
82 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
83 Self::Sqlite(s) => s.contains(table, key, version),
84 }
85 }
86
87 #[inline]
88 fn set(
89 &self,
90 version: CommitVersion,
91 batches: HashMap<EntryKind, Vec<(CowVec<u8>, Option<CowVec<u8>>)>>,
92 ) -> Result<()> {
93 match self {
94 Self::Memory(s) => s.set(version, batches),
95 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
96 Self::Sqlite(s) => s.set(version, batches),
97 }
98 }
99
100 #[inline]
101 fn range_next(
102 &self,
103 table: EntryKind,
104 cursor: &mut RangeCursor,
105 start: Bound<&[u8]>,
106 end: Bound<&[u8]>,
107 version: CommitVersion,
108 batch_size: usize,
109 ) -> Result<RangeBatch> {
110 match self {
111 Self::Memory(s) => s.range_next(table, cursor, start, end, version, batch_size),
112 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
113 Self::Sqlite(s) => s.range_next(table, cursor, start, end, version, batch_size),
114 }
115 }
116
117 #[inline]
118 fn range_rev_next(
119 &self,
120 table: EntryKind,
121 cursor: &mut RangeCursor,
122 start: Bound<&[u8]>,
123 end: Bound<&[u8]>,
124 version: CommitVersion,
125 batch_size: usize,
126 ) -> Result<RangeBatch> {
127 match self {
128 Self::Memory(s) => s.range_rev_next(table, cursor, start, end, version, batch_size),
129 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
130 Self::Sqlite(s) => s.range_rev_next(table, cursor, start, end, version, batch_size),
131 }
132 }
133
134 #[inline]
135 fn ensure_table(&self, table: EntryKind) -> Result<()> {
136 match self {
137 Self::Memory(s) => s.ensure_table(table),
138 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
139 Self::Sqlite(s) => s.ensure_table(table),
140 }
141 }
142
143 #[inline]
144 fn clear_table(&self, table: EntryKind) -> Result<()> {
145 match self {
146 Self::Memory(s) => s.clear_table(table),
147 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
148 Self::Sqlite(s) => s.clear_table(table),
149 }
150 }
151
152 #[inline]
153 fn drop(&self, batches: HashMap<EntryKind, Vec<(CowVec<u8>, CommitVersion)>>) -> Result<()> {
154 match self {
155 Self::Memory(s) => s.drop(batches),
156 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
157 Self::Sqlite(s) => s.drop(batches),
158 }
159 }
160
161 #[inline]
162 fn get_all_versions(&self, table: EntryKind, key: &[u8]) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>> {
163 match self {
164 Self::Memory(s) => s.get_all_versions(table, key),
165 #[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
166 Self::Sqlite(s) => s.get_all_versions(table, key),
167 }
168 }
169}
170
171impl TierBackend for HotStorage {}
172
173#[cfg(test)]
174pub mod tests {
175 use super::*;
176
177 #[test]
178 fn test_memory_backend() {
179 let storage = HotStorage::memory();
180
181 let key = CowVec::new(b"key".to_vec());
182 let version = CommitVersion(1);
183
184 storage.set(
185 version,
186 HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"value".to_vec())))])]),
187 )
188 .unwrap();
189 assert_eq!(storage.get(EntryKind::Multi, &key, version).unwrap().as_deref(), Some(b"value".as_slice()));
190 }
191
192 #[test]
193 fn test_sqlite_backend() {
194 let storage = HotStorage::sqlite_in_memory();
195
196 let key = CowVec::new(b"key".to_vec());
197 let version = CommitVersion(1);
198
199 storage.set(
200 version,
201 HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"value".to_vec())))])]),
202 )
203 .unwrap();
204 assert_eq!(storage.get(EntryKind::Multi, &key, version).unwrap().as_deref(), Some(b"value".as_slice()));
205 }
206
207 #[test]
208 fn test_range_next_memory() {
209 let storage = HotStorage::memory();
210
211 let version = CommitVersion(1);
212 storage.set(
213 version,
214 HashMap::from([(
215 EntryKind::Multi,
216 vec![
217 (CowVec::new(b"a".to_vec()), Some(CowVec::new(b"1".to_vec()))),
218 (CowVec::new(b"b".to_vec()), Some(CowVec::new(b"2".to_vec()))),
219 (CowVec::new(b"c".to_vec()), Some(CowVec::new(b"3".to_vec()))),
220 ],
221 )]),
222 )
223 .unwrap();
224
225 let mut cursor = RangeCursor::new();
226 let batch = storage
227 .range_next(EntryKind::Multi, &mut cursor, Bound::Unbounded, Bound::Unbounded, version, 100)
228 .unwrap();
229
230 assert_eq!(batch.entries.len(), 3);
231 assert!(!batch.has_more);
232 assert!(cursor.exhausted);
233 }
234
235 #[test]
236 fn test_range_next_sqlite() {
237 let storage = HotStorage::sqlite_in_memory();
238
239 let version = CommitVersion(1);
240 storage.set(
241 version,
242 HashMap::from([(
243 EntryKind::Multi,
244 vec![
245 (CowVec::new(b"a".to_vec()), Some(CowVec::new(b"1".to_vec()))),
246 (CowVec::new(b"b".to_vec()), Some(CowVec::new(b"2".to_vec()))),
247 (CowVec::new(b"c".to_vec()), Some(CowVec::new(b"3".to_vec()))),
248 ],
249 )]),
250 )
251 .unwrap();
252
253 let mut cursor = RangeCursor::new();
254 let batch = storage
255 .range_next(EntryKind::Multi, &mut cursor, Bound::Unbounded, Bound::Unbounded, version, 100)
256 .unwrap();
257
258 assert_eq!(batch.entries.len(), 3);
259 assert!(!batch.has_more);
260 assert!(cursor.exhausted);
261 }
262}