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. Holds the durable, version-history-bearing record of every key the
5//! buffer has flushed. The default backend is SQLite; the trait surface is generic so other backends can be
6//! plugged in without touching the buffer or transaction layer.
7
8use std::{collections::HashMap, 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, util::cowvec::CowVec};
16
17use crate::{
18	MultiVersionScope,
19	tier::{
20		HistoricalCursor, 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::SqlitePersistentStorage;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct CheckpointOutcome {
33	pub log_frames: u32,
34	pub restarted: bool,
35}
36
37#[derive(Clone)]
38#[cfg_attr(all(feature = "sqlite", not(target_arch = "wasm32")), repr(u8))]
39pub enum MultiPersistentTier {
40	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
41	Sqlite(SqlitePersistentStorage) = 0,
42}
43
44impl Shutdown for MultiPersistentTier {
45	fn shutdown(&self) {
46		match self {
47			#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
48			Self::Sqlite(s) => s.shutdown(),
49			#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
50			_ => {}
51		}
52	}
53}
54
55#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
56impl MultiPersistentTier {
57	pub fn sqlite(config: SqliteConfig) -> Self {
58		Self::Sqlite(SqlitePersistentStorage::new(config))
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 maybe_checkpoint(&self) -> Result<CheckpointOutcome> {
67		match self {
68			Self::Sqlite(s) => s.maybe_checkpoint(),
69		}
70	}
71
72	pub fn set_checkpoint_threshold(&self, frames: u32) {
73		match self {
74			Self::Sqlite(s) => s.set_checkpoint_threshold(frames),
75		}
76	}
77
78	pub fn delete_below_version(
79		&self,
80		table: EntryKind,
81		cutoff_version: CommitVersion,
82		prefix: Option<&[u8]>,
83	) -> Result<Vec<EncodedKey>> {
84		match self {
85			Self::Sqlite(s) => s.delete_below_version(table, cutoff_version, prefix),
86		}
87	}
88
89	pub fn delete_keys(&self, table: EntryKind, keys: &[EncodedKey]) -> Result<u64> {
90		match self {
91			Self::Sqlite(s) => s.delete_keys(table, keys),
92		}
93	}
94
95	pub fn set_collecting_accepted(&self, version: CommitVersion, batches: TierBatch) -> Result<Vec<EncodedKey>> {
96		match self {
97			Self::Sqlite(s) => s.set_collecting_accepted(version, batches),
98		}
99	}
100
101	pub fn persist_sweep(&self, batches: Vec<(CommitVersion, TierBatch)>) -> Result<Vec<EncodedKey>> {
102		match self {
103			Self::Sqlite(s) => s.persist_sweep(batches),
104		}
105	}
106
107	pub fn load_range_consistent(
108		&self,
109		table: EntryKind,
110		start: Bound<&[u8]>,
111		end: Bound<&[u8]>,
112		read: CommitVersion,
113		limit: Option<usize>,
114	) -> Result<Vec<RawEntry>> {
115		match self {
116			Self::Sqlite(s) => s.load_range_consistent(table, start, end, read, limit),
117		}
118	}
119
120	pub fn delete_keys_through(&self, table: EntryKind, keys: &[(EncodedKey, CommitVersion)]) -> Result<u64> {
121		match self {
122			Self::Sqlite(s) => s.delete_keys_through(table, keys),
123		}
124	}
125}
126
127#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
128impl MultiPersistentTier {
129	pub fn maybe_checkpoint(&self) -> Result<CheckpointOutcome> {
130		match *self {}
131	}
132
133	pub fn set_checkpoint_threshold(&self, _frames: u32) {
134		match *self {}
135	}
136
137	pub fn delete_below_version(
138		&self,
139		_table: EntryKind,
140		_cutoff_version: CommitVersion,
141		_prefix: Option<&[u8]>,
142	) -> Result<Vec<EncodedKey>> {
143		match *self {}
144	}
145
146	pub fn delete_keys(&self, _table: EntryKind, _keys: &[EncodedKey]) -> Result<u64> {
147		match *self {}
148	}
149
150	pub fn persist_sweep(&self, _batches: Vec<(CommitVersion, TierBatch)>) -> Result<Vec<EncodedKey>> {
151		match *self {}
152	}
153
154	pub fn load_range_consistent(
155		&self,
156		_table: EntryKind,
157		_start: Bound<&[u8]>,
158		_end: Bound<&[u8]>,
159		_read: CommitVersion,
160		_limit: Option<usize>,
161	) -> Result<Vec<RawEntry>> {
162		match *self {}
163	}
164
165	pub fn delete_keys_through(&self, _table: EntryKind, _keys: &[(EncodedKey, CommitVersion)]) -> Result<u64> {
166		match *self {}
167	}
168}
169
170#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
171impl TierStorage for MultiPersistentTier {
172	fn get(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<VersionedGetResult> {
173		match self {
174			Self::Sqlite(s) => s.get(table, key, version),
175		}
176	}
177
178	fn get_many(
179		&self,
180		table: EntryKind,
181		keys: &[&[u8]],
182		version: CommitVersion,
183	) -> Result<Vec<VersionedGetResult>> {
184		match self {
185			Self::Sqlite(s) => s.get_many(table, keys, version),
186		}
187	}
188
189	fn set(&self, version: CommitVersion, batches: TierBatch) -> Result<()> {
190		match self {
191			Self::Sqlite(s) => s.set(version, batches),
192		}
193	}
194
195	fn range_next(
196		&self,
197		table: EntryKind,
198		cursor: &mut RangeCursor,
199		start: Bound<&[u8]>,
200		end: Bound<&[u8]>,
201		scope: MultiVersionScope,
202		batch_size: usize,
203	) -> Result<RangeBatch> {
204		match self {
205			Self::Sqlite(s) => s.range_next(table, cursor, start, end, scope, batch_size),
206		}
207	}
208
209	fn range_rev_next(
210		&self,
211		table: EntryKind,
212		cursor: &mut RangeCursor,
213		start: Bound<&[u8]>,
214		end: Bound<&[u8]>,
215		scope: MultiVersionScope,
216		batch_size: usize,
217	) -> Result<RangeBatch> {
218		match self {
219			Self::Sqlite(s) => s.range_rev_next(table, cursor, start, end, scope, batch_size),
220		}
221	}
222
223	fn ensure_table(&self, table: EntryKind) -> Result<()> {
224		match self {
225			Self::Sqlite(s) => s.ensure_table(table),
226		}
227	}
228
229	fn clear_table(&self, table: EntryKind) -> Result<()> {
230		match self {
231			Self::Sqlite(s) => s.clear_table(table),
232		}
233	}
234
235	fn drop(&self, batches: HashMap<EntryKind, Vec<(EncodedKey, CommitVersion)>>) -> Result<()> {
236		match self {
237			Self::Sqlite(s) => s.drop(batches),
238		}
239	}
240
241	fn get_all_versions(&self, table: EntryKind, key: &[u8]) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>> {
242		match self {
243			Self::Sqlite(s) => s.get_all_versions(table, key),
244		}
245	}
246
247	fn scan_historical_below(
248		&self,
249		table: EntryKind,
250		cutoff: CommitVersion,
251		cursor: &mut HistoricalCursor,
252		batch_size: usize,
253	) -> Result<Vec<(EncodedKey, CommitVersion)>> {
254		match self {
255			Self::Sqlite(s) => s.scan_historical_below(table, cutoff, cursor, batch_size),
256		}
257	}
258}
259
260#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
261impl TierStorage for MultiPersistentTier {
262	fn get(&self, _table: EntryKind, _key: &[u8], _version: CommitVersion) -> Result<VersionedGetResult> {
263		match *self {}
264	}
265
266	fn set(&self, _version: CommitVersion, _batches: TierBatch) -> Result<()> {
267		match *self {}
268	}
269
270	fn range_next(
271		&self,
272		_table: EntryKind,
273		_cursor: &mut RangeCursor,
274		_start: Bound<&[u8]>,
275		_end: Bound<&[u8]>,
276		_scope: MultiVersionScope,
277		_batch_size: usize,
278	) -> Result<RangeBatch> {
279		match *self {}
280	}
281
282	fn range_rev_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 ensure_table(&self, _table: EntryKind) -> Result<()> {
295		match *self {}
296	}
297
298	fn clear_table(&self, _table: EntryKind) -> Result<()> {
299		match *self {}
300	}
301
302	fn drop(&self, _batches: HashMap<EntryKind, Vec<(EncodedKey, CommitVersion)>>) -> Result<()> {
303		match *self {}
304	}
305
306	fn get_all_versions(&self, _table: EntryKind, _key: &[u8]) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>> {
307		match *self {}
308	}
309
310	fn scan_historical_below(
311		&self,
312		_table: EntryKind,
313		_cutoff: CommitVersion,
314		_cursor: &mut HistoricalCursor,
315		_batch_size: usize,
316	) -> Result<Vec<(EncodedKey, CommitVersion)>> {
317		match *self {}
318	}
319}
320
321impl TierBackend for MultiPersistentTier {}