Skip to main content

reifydb_store_multi/tier/persistent/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4//! Cold tier of the multi-version store: one durable row per key, carrying the latest version the sweep
5//! flushed, not a version chain. The default backend is SQLite; the trait surface is generic so another
6//! backend can be plugged in without touching the buffer or transaction layer.
7
8use std::ops::Bound;
9
10use reifydb_codec::key::encoded::EncodedKey;
11use reifydb_core::{common::CommitVersion, interface::store::EntryKind};
12use reifydb_runtime::shutdown::Shutdown;
13#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
14use reifydb_sqlite::{SqliteConfig, SqliteTempPathGuard};
15use reifydb_value::{Result, value::datetime::DateTime};
16
17use crate::{
18	MultiVersionScope,
19	tier::{
20		DisplacedValues, RangeBatch, RangeCursor, RawEntry, TierBackend, TierBatch, TierStorage,
21		VersionedGetResult,
22	},
23};
24
25#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
26pub mod sqlite;
27
28#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
29use sqlite::storage::{SqlitePageCacheMetrics, SqlitePersistentStorage};
30
31#[derive(Clone)]
32#[cfg_attr(all(feature = "sqlite", not(target_arch = "wasm32")), repr(u8))]
33pub enum MultiPersistentTier {
34	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
35	Sqlite(SqlitePersistentStorage) = 0,
36}
37
38impl Shutdown for MultiPersistentTier {
39	fn shutdown(&self) {
40		match self {
41			#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
42			Self::Sqlite(s) => s.shutdown(),
43			#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
44			_ => {}
45		}
46	}
47}
48
49#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
50impl MultiPersistentTier {
51	pub fn sqlite(config: SqliteConfig) -> Self {
52		Self::Sqlite(SqlitePersistentStorage::new(config))
53	}
54
55	pub fn page_cache_metrics(&self) -> SqlitePageCacheMetrics {
56		match self {
57			Self::Sqlite(storage) => storage.page_cache_metrics(),
58		}
59	}
60
61	pub fn sqlite_in_memory() -> (Self, SqliteTempPathGuard) {
62		let (storage, guard) = SqlitePersistentStorage::in_memory();
63		(Self::Sqlite(storage), guard)
64	}
65
66	pub fn set_checkpoint_threshold(&self, frames: u32) {
67		match self {
68			Self::Sqlite(s) => s.set_checkpoint_threshold(frames),
69		}
70	}
71
72	pub fn delete_below_version(
73		&self,
74		table: EntryKind,
75		cutoff_version: CommitVersion,
76		prefix: Option<&[u8]>,
77		cursor: Option<&[u8]>,
78		limit: usize,
79	) -> Result<(Vec<EncodedKey>, Option<EncodedKey>)> {
80		match self {
81			Self::Sqlite(s) => s.delete_below_version(table, cutoff_version, prefix, cursor, limit),
82		}
83	}
84
85	pub fn delete_keys(&self, table: EntryKind, keys: &[EncodedKey]) -> Result<u64> {
86		match self {
87			Self::Sqlite(s) => s.delete_keys(table, keys),
88		}
89	}
90
91	pub fn expired_keys(
92		&self,
93		table: EntryKind,
94		cutoff: DateTime,
95		cursor: Option<(DateTime, &[u8])>,
96		limit: usize,
97	) -> Result<Vec<(EncodedKey, DateTime)>> {
98		match self {
99			Self::Sqlite(s) => s.expired_keys(table, cutoff, cursor, limit),
100		}
101	}
102
103	pub fn list_current_entries(&self) -> Result<Vec<EntryKind>> {
104		match self {
105			Self::Sqlite(s) => s.list_current_entries(),
106		}
107	}
108
109	pub fn reap_tombstones(
110		&self,
111		kind: EntryKind,
112		cutoff_version: CommitVersion,
113		limit: usize,
114	) -> Result<(u64, bool)> {
115		match self {
116			Self::Sqlite(s) => s.reap_tombstones(kind, cutoff_version, limit),
117		}
118	}
119
120	pub fn set_collecting_accepted(&self, version: CommitVersion, batches: TierBatch) -> Result<Vec<EncodedKey>> {
121		match self {
122			Self::Sqlite(s) => s.set_collecting_accepted(version, batches),
123		}
124	}
125
126	pub fn persist_sweep(&self, batches: Vec<(CommitVersion, TierBatch)>) -> Result<Vec<EncodedKey>> {
127		match self {
128			Self::Sqlite(s) => s.persist_sweep(batches),
129		}
130	}
131
132	pub fn load_range_consistent(
133		&self,
134		table: EntryKind,
135		start: Bound<&[u8]>,
136		end: Bound<&[u8]>,
137		read: CommitVersion,
138		limit: Option<usize>,
139	) -> Result<Vec<RawEntry>> {
140		match self {
141			Self::Sqlite(s) => s.load_range_consistent(table, start, end, read, limit),
142		}
143	}
144}
145
146#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
147impl MultiPersistentTier {
148	pub fn set_checkpoint_threshold(&self, _frames: u32) {
149		match *self {}
150	}
151
152	pub fn delete_below_version(
153		&self,
154		_table: EntryKind,
155		_cutoff_version: CommitVersion,
156		_prefix: Option<&[u8]>,
157		_cursor: Option<&[u8]>,
158		_limit: usize,
159	) -> Result<(Vec<EncodedKey>, Option<EncodedKey>)> {
160		match *self {}
161	}
162
163	pub fn delete_keys(&self, _table: EntryKind, _keys: &[EncodedKey]) -> Result<u64> {
164		match *self {}
165	}
166
167	pub fn expired_keys(
168		&self,
169		_table: EntryKind,
170		_cutoff: DateTime,
171		_cursor: Option<(DateTime, &[u8])>,
172		_limit: usize,
173	) -> Result<Vec<(EncodedKey, DateTime)>> {
174		match *self {}
175	}
176
177	pub fn list_current_entries(&self) -> Result<Vec<EntryKind>> {
178		match *self {}
179	}
180
181	pub fn reap_tombstones(
182		&self,
183		_kind: EntryKind,
184		_cutoff_version: CommitVersion,
185		_limit: usize,
186	) -> Result<(u64, bool)> {
187		match *self {}
188	}
189
190	pub fn persist_sweep(&self, _batches: Vec<(CommitVersion, TierBatch)>) -> Result<Vec<EncodedKey>> {
191		match *self {}
192	}
193
194	pub fn load_range_consistent(
195		&self,
196		_table: EntryKind,
197		_start: Bound<&[u8]>,
198		_end: Bound<&[u8]>,
199		_read: CommitVersion,
200		_limit: Option<usize>,
201	) -> Result<Vec<RawEntry>> {
202		match *self {}
203	}
204}
205
206#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
207impl TierStorage for MultiPersistentTier {
208	fn get(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<VersionedGetResult> {
209		match self {
210			Self::Sqlite(s) => s.get(table, key, version),
211		}
212	}
213
214	fn get_many(
215		&self,
216		table: EntryKind,
217		keys: &[&[u8]],
218		version: CommitVersion,
219	) -> Result<Vec<VersionedGetResult>> {
220		match self {
221			Self::Sqlite(s) => s.get_many(table, keys, version),
222		}
223	}
224
225	fn set(&self, version: CommitVersion, batches: TierBatch) -> Result<DisplacedValues> {
226		match self {
227			Self::Sqlite(s) => s.set(version, batches),
228		}
229	}
230
231	fn range_next(
232		&self,
233		table: EntryKind,
234		cursor: &mut RangeCursor,
235		start: Bound<&[u8]>,
236		end: Bound<&[u8]>,
237		scope: MultiVersionScope,
238		batch_size: usize,
239	) -> Result<RangeBatch> {
240		match self {
241			Self::Sqlite(s) => s.range_next(table, cursor, start, end, scope, batch_size),
242		}
243	}
244
245	fn range_rev_next(
246		&self,
247		table: EntryKind,
248		cursor: &mut RangeCursor,
249		start: Bound<&[u8]>,
250		end: Bound<&[u8]>,
251		scope: MultiVersionScope,
252		batch_size: usize,
253	) -> Result<RangeBatch> {
254		match self {
255			Self::Sqlite(s) => s.range_rev_next(table, cursor, start, end, scope, batch_size),
256		}
257	}
258
259	fn ensure_table(&self, table: EntryKind) -> Result<()> {
260		match self {
261			Self::Sqlite(s) => s.ensure_table(table),
262		}
263	}
264
265	fn clear_table(&self, table: EntryKind) -> Result<()> {
266		match self {
267			Self::Sqlite(s) => s.clear_table(table),
268		}
269	}
270}
271
272#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
273impl TierStorage for MultiPersistentTier {
274	fn get(&self, _table: EntryKind, _key: &[u8], _version: CommitVersion) -> Result<VersionedGetResult> {
275		match *self {}
276	}
277
278	fn set(&self, _version: CommitVersion, _batches: TierBatch) -> Result<DisplacedValues> {
279		match *self {}
280	}
281
282	fn range_next(
283		&self,
284		_table: EntryKind,
285		_cursor: &mut RangeCursor,
286		_start: Bound<&[u8]>,
287		_end: Bound<&[u8]>,
288		_scope: MultiVersionScope,
289		_batch_size: usize,
290	) -> Result<RangeBatch> {
291		match *self {}
292	}
293
294	fn range_rev_next(
295		&self,
296		_table: EntryKind,
297		_cursor: &mut RangeCursor,
298		_start: Bound<&[u8]>,
299		_end: Bound<&[u8]>,
300		_scope: MultiVersionScope,
301		_batch_size: usize,
302	) -> Result<RangeBatch> {
303		match *self {}
304	}
305
306	fn ensure_table(&self, _table: EntryKind) -> Result<()> {
307		match *self {}
308	}
309
310	fn clear_table(&self, _table: EntryKind) -> Result<()> {
311		match *self {}
312	}
313}
314
315impl TierBackend for MultiPersistentTier {}