Skip to main content

reifydb_store_multi/tier/persistent/
mod.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
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_core::{common::CommitVersion, encoded::key::EncodedKey, interface::store::EntryKind, row::TtlAnchor};
11#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
12use reifydb_sqlite::{SqliteConfig, SqliteTempPathGuard};
13use reifydb_value::{Result, util::cowvec::CowVec};
14
15use crate::tier::{HistoricalCursor, RangeBatch, RangeCursor, TierBackend, TierBatch, TierStorage, VersionedGetResult};
16
17#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
18pub mod sqlite;
19
20#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
21use sqlite::storage::SqlitePersistentStorage;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct CheckpointOutcome {
25	pub log_frames: u32,
26	pub restarted: bool,
27}
28
29#[derive(Clone)]
30#[cfg_attr(all(feature = "sqlite", not(target_arch = "wasm32")), repr(u8))]
31pub enum MultiPersistentTier {
32	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
33	Sqlite(SqlitePersistentStorage) = 0,
34}
35
36#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
37impl MultiPersistentTier {
38	pub fn sqlite(config: SqliteConfig) -> Self {
39		Self::Sqlite(SqlitePersistentStorage::new(config))
40	}
41
42	pub fn sqlite_in_memory() -> (Self, SqliteTempPathGuard) {
43		let (storage, guard) = SqlitePersistentStorage::in_memory();
44		(Self::Sqlite(storage), guard)
45	}
46
47	pub fn maybe_checkpoint(&self) -> Result<CheckpointOutcome> {
48		match self {
49			Self::Sqlite(s) => s.maybe_checkpoint(),
50		}
51	}
52
53	pub fn delete_expired(
54		&self,
55		table: EntryKind,
56		anchor: TtlAnchor,
57		cutoff_nanos: u64,
58		prefix: Option<&[u8]>,
59	) -> Result<u64> {
60		match self {
61			Self::Sqlite(s) => s.delete_expired(table, anchor, cutoff_nanos, prefix),
62		}
63	}
64
65	pub fn delete_keys(&self, table: EntryKind, keys: &[EncodedKey]) -> Result<u64> {
66		match self {
67			Self::Sqlite(s) => s.delete_keys(table, keys),
68		}
69	}
70}
71
72#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
73impl MultiPersistentTier {
74	pub fn maybe_checkpoint(&self) -> Result<CheckpointOutcome> {
75		match *self {}
76	}
77
78	pub fn delete_expired(
79		&self,
80		_table: EntryKind,
81		_anchor: TtlAnchor,
82		_cutoff_nanos: u64,
83		_prefix: Option<&[u8]>,
84	) -> Result<u64> {
85		match *self {}
86	}
87
88	pub fn delete_keys(&self, _table: EntryKind, _keys: &[EncodedKey]) -> Result<u64> {
89		match *self {}
90	}
91}
92
93#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
94impl TierStorage for MultiPersistentTier {
95	fn get(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<VersionedGetResult> {
96		match self {
97			Self::Sqlite(s) => s.get(table, key, version),
98		}
99	}
100
101	fn get_many(
102		&self,
103		table: EntryKind,
104		keys: &[&[u8]],
105		version: CommitVersion,
106	) -> Result<Vec<VersionedGetResult>> {
107		match self {
108			Self::Sqlite(s) => s.get_many(table, keys, version),
109		}
110	}
111
112	fn set(&self, version: CommitVersion, batches: TierBatch) -> Result<()> {
113		match self {
114			Self::Sqlite(s) => s.set(version, batches),
115		}
116	}
117
118	fn range_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::Sqlite(s) => s.range_next(table, cursor, start, end, version, batch_size),
129		}
130	}
131
132	fn range_rev_next(
133		&self,
134		table: EntryKind,
135		cursor: &mut RangeCursor,
136		start: Bound<&[u8]>,
137		end: Bound<&[u8]>,
138		version: CommitVersion,
139		batch_size: usize,
140	) -> Result<RangeBatch> {
141		match self {
142			Self::Sqlite(s) => s.range_rev_next(table, cursor, start, end, version, batch_size),
143		}
144	}
145
146	fn ensure_table(&self, table: EntryKind) -> Result<()> {
147		match self {
148			Self::Sqlite(s) => s.ensure_table(table),
149		}
150	}
151
152	fn clear_table(&self, table: EntryKind) -> Result<()> {
153		match self {
154			Self::Sqlite(s) => s.clear_table(table),
155		}
156	}
157
158	fn drop(&self, batches: HashMap<EntryKind, Vec<(EncodedKey, CommitVersion)>>) -> Result<()> {
159		match self {
160			Self::Sqlite(s) => s.drop(batches),
161		}
162	}
163
164	fn get_all_versions(&self, table: EntryKind, key: &[u8]) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>> {
165		match self {
166			Self::Sqlite(s) => s.get_all_versions(table, key),
167		}
168	}
169
170	fn scan_historical_below(
171		&self,
172		table: EntryKind,
173		cutoff: CommitVersion,
174		cursor: &mut HistoricalCursor,
175		batch_size: usize,
176	) -> Result<Vec<(EncodedKey, CommitVersion)>> {
177		match self {
178			Self::Sqlite(s) => s.scan_historical_below(table, cutoff, cursor, batch_size),
179		}
180	}
181}
182
183#[cfg(not(all(feature = "sqlite", not(target_arch = "wasm32"))))]
184impl TierStorage for MultiPersistentTier {
185	fn get(&self, _table: EntryKind, _key: &[u8], _version: CommitVersion) -> Result<VersionedGetResult> {
186		match *self {}
187	}
188
189	fn set(&self, _version: CommitVersion, _batches: TierBatch) -> Result<()> {
190		match *self {}
191	}
192
193	fn range_next(
194		&self,
195		_table: EntryKind,
196		_cursor: &mut RangeCursor,
197		_start: Bound<&[u8]>,
198		_end: Bound<&[u8]>,
199		_version: CommitVersion,
200		_batch_size: usize,
201	) -> Result<RangeBatch> {
202		match *self {}
203	}
204
205	fn range_rev_next(
206		&self,
207		_table: EntryKind,
208		_cursor: &mut RangeCursor,
209		_start: Bound<&[u8]>,
210		_end: Bound<&[u8]>,
211		_version: CommitVersion,
212		_batch_size: usize,
213	) -> Result<RangeBatch> {
214		match *self {}
215	}
216
217	fn ensure_table(&self, _table: EntryKind) -> Result<()> {
218		match *self {}
219	}
220
221	fn clear_table(&self, _table: EntryKind) -> Result<()> {
222		match *self {}
223	}
224
225	fn drop(&self, _batches: HashMap<EntryKind, Vec<(EncodedKey, CommitVersion)>>) -> Result<()> {
226		match *self {}
227	}
228
229	fn get_all_versions(&self, _table: EntryKind, _key: &[u8]) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>> {
230		match *self {}
231	}
232
233	fn scan_historical_below(
234		&self,
235		_table: EntryKind,
236		_cutoff: CommitVersion,
237		_cursor: &mut HistoricalCursor,
238		_batch_size: usize,
239	) -> Result<Vec<(EncodedKey, CommitVersion)>> {
240		match *self {}
241	}
242}
243
244impl TierBackend for MultiPersistentTier {}