Skip to main content

reifydb_store_multi/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4//! Multi-version storage backend for OLTP traffic. Implements the `MultiVersionStore` family of traits from
5//! `core::interface::store` so the engine can read at a snapshot, write a new version, and step backwards through
6//! history without coordinating with concurrent readers.
7//!
8//! The backend is tiered: hot writes land in the in-memory buffer, the flusher migrates them to persistent storage
9//! at commit boundaries, and the garbage collector reclaims versions that have aged out behind the configured
10//! retention. The persistent tier is pluggable - a SQLite-backed implementation is the default but the trait surface
11//! is what the engine binds to, so other backends can be slotted in.
12//!
13//! Invariant: a row at `version V` is the value visible to a reader whose snapshot is `>= V` and where no later
14//! version exists at `V' <= snapshot`. Commit must publish all deltas of a transaction atomically with respect to
15//! readers; partial visibility breaks snapshot isolation.
16
17#![cfg_attr(not(debug_assertions), deny(clippy::disallowed_methods))]
18#![cfg_attr(debug_assertions, warn(clippy::disallowed_methods))]
19#![cfg_attr(not(debug_assertions), deny(warnings))]
20#![allow(clippy::tabs_in_doc_comments)]
21
22use reifydb_core::{
23	event::EventBus,
24	interface::version::{ComponentType, HasVersion, SystemVersion},
25};
26use reifydb_value::Result;
27
28pub mod flush;
29pub mod gc;
30pub mod tier;
31
32pub mod config;
33pub mod store;
34
35use std::collections::HashMap;
36
37use config::{CommitBufferConfig, MultiStoreConfig};
38use reifydb_core::{
39	common::CommitVersion,
40	delta::Delta,
41	encoded::key::{EncodedKey, EncodedKeyRange},
42	interface::store::{
43		MultiVersionCommit, MultiVersionContains, MultiVersionGet, MultiVersionGetPrevious, MultiVersionRow,
44		MultiVersionStore,
45	},
46};
47use reifydb_runtime::shutdown::Shutdown;
48#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
49use reifydb_sqlite::SqliteTempPathGuard;
50use reifydb_value::util::cowvec::CowVec;
51use store::StandardMultiStore;
52
53pub mod memory {}
54pub mod sqlite {}
55
56pub struct MultiStoreVersion;
57
58impl HasVersion for MultiStoreVersion {
59	fn version(&self) -> SystemVersion {
60		SystemVersion {
61			name: env!("CARGO_PKG_NAME")
62				.strip_prefix("reifydb-")
63				.unwrap_or(env!("CARGO_PKG_NAME"))
64				.to_string(),
65			version: env!("CARGO_PKG_VERSION").to_string(),
66			description: "Multi-version storage for OLTP operations with MVCC support".to_string(),
67			r#type: ComponentType::Module,
68		}
69	}
70}
71
72#[repr(u8)]
73#[derive(Clone)]
74pub enum MultiStore {
75	Standard(StandardMultiStore) = 0,
76}
77
78impl MultiStore {
79	pub fn standard(config: MultiStoreConfig) -> Self {
80		Self::Standard(StandardMultiStore::new(config).unwrap())
81	}
82}
83
84impl MultiStore {
85	pub fn testing_memory() -> Self {
86		MultiStore::Standard(StandardMultiStore::testing_memory())
87	}
88
89	pub fn testing_memory_with_eventbus(event_bus: EventBus) -> Self {
90		MultiStore::Standard(StandardMultiStore::testing_memory_with_eventbus(event_bus))
91	}
92
93	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
94	pub fn testing_memory_with_persistent_sqlite() -> (Self, SqliteTempPathGuard) {
95		let (store, guard) = StandardMultiStore::testing_memory_with_persistent_sqlite();
96		(MultiStore::Standard(store), guard)
97	}
98
99	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
100	pub fn testing_memory_with_persistent_sqlite_with_eventbus(event_bus: EventBus) -> (Self, SqliteTempPathGuard) {
101		let (store, guard) = StandardMultiStore::testing_memory_with_persistent_sqlite_with_eventbus(event_bus);
102		(MultiStore::Standard(store), guard)
103	}
104
105	pub fn flush_pending_blocking(&self) {
106		match self {
107			MultiStore::Standard(store) => store.flush_pending_blocking(),
108		}
109	}
110
111	pub fn flush_all_blocking(&self) {
112		match self {
113			MultiStore::Standard(store) => store.flush_all_blocking(),
114		}
115	}
116
117	pub fn commit(&self) -> Option<&tier::commit::buffer::MultiCommitBufferTier> {
118		match self {
119			MultiStore::Standard(store) => store.commit(),
120		}
121	}
122
123	pub fn persistent(&self) -> Option<&tier::persistent::MultiPersistentTier> {
124		match self {
125			MultiStore::Standard(store) => store.persistent(),
126		}
127	}
128
129	pub fn clear_eviction_watermark(&self) {
130		match self {
131			MultiStore::Standard(store) => store.clear_eviction_watermark(),
132		}
133	}
134}
135
136impl Shutdown for MultiStore {
137	fn shutdown(&self) {
138		match self {
139			MultiStore::Standard(store) => store.shutdown(),
140		}
141	}
142}
143
144impl MultiVersionGet for MultiStore {
145	#[inline]
146	fn get(&self, key: &EncodedKey, version: CommitVersion) -> Result<Option<MultiVersionRow>> {
147		match self {
148			MultiStore::Standard(store) => MultiVersionGet::get(store, key, version),
149		}
150	}
151}
152
153impl MultiVersionContains for MultiStore {
154	#[inline]
155	fn contains(&self, key: &EncodedKey, version: CommitVersion) -> Result<bool> {
156		match self {
157			MultiStore::Standard(store) => MultiVersionContains::contains(store, key, version),
158		}
159	}
160}
161
162impl MultiVersionCommit for MultiStore {
163	#[inline]
164	fn commit(&self, deltas: CowVec<Delta>, version: CommitVersion) -> Result<()> {
165		match self {
166			MultiStore::Standard(store) => MultiVersionCommit::commit(store, deltas, version),
167		}
168	}
169}
170
171impl MultiVersionGetPrevious for MultiStore {
172	#[inline]
173	fn get_previous_version(
174		&self,
175		key: &EncodedKey,
176		before_version: CommitVersion,
177	) -> Result<Option<MultiVersionRow>> {
178		match self {
179			MultiStore::Standard(store) => store.get_previous_version(key, before_version),
180		}
181	}
182}
183
184pub type MultiVersionRangeIterator<'a> = Box<dyn Iterator<Item = Result<MultiVersionRow>> + Send + 'a>;
185
186/// Version scope for a multi-version range scan.
187///
188/// Selects which version is returned for each key during a range walk.
189#[derive(Debug, Copy, Clone, PartialEq, Eq)]
190pub enum MultiVersionScope {
191	/// For each key, yield the highest version `v` with `v <= read`.
192	AsOf {
193		read: CommitVersion,
194	},
195	/// For each key, yield the highest version `v` with `after < v <= read`.
196	/// Keys with no qualifying version are dropped from the output.
197	Between {
198		after: CommitVersion,
199		read: CommitVersion,
200	},
201}
202
203impl MultiVersionScope {
204	#[inline]
205	pub fn read(&self) -> CommitVersion {
206		match self {
207			Self::AsOf {
208				read,
209			}
210			| Self::Between {
211				read,
212				..
213			} => *read,
214		}
215	}
216
217	#[inline]
218	pub fn contains(&self, v: CommitVersion) -> bool {
219		match self {
220			Self::AsOf {
221				read,
222			} => v <= *read,
223			Self::Between {
224				after,
225				read,
226			} => v > *after && v <= *read,
227		}
228	}
229}
230
231impl MultiStore {
232	pub fn range(
233		&self,
234		range: EncodedKeyRange,
235		scope: MultiVersionScope,
236		batch_size: usize,
237	) -> MultiVersionRangeIterator<'_> {
238		match self {
239			MultiStore::Standard(store) => Box::new(store.range(range, scope, batch_size)),
240		}
241	}
242
243	pub fn range_rev(
244		&self,
245		range: EncodedKeyRange,
246		scope: MultiVersionScope,
247		batch_size: usize,
248	) -> MultiVersionRangeIterator<'_> {
249		match self {
250			MultiStore::Standard(store) => Box::new(store.range_rev(range, scope, batch_size)),
251		}
252	}
253
254	pub fn get_many(
255		&self,
256		keys: &[EncodedKey],
257		version: CommitVersion,
258	) -> Result<HashMap<EncodedKey, MultiVersionRow>> {
259		match self {
260			MultiStore::Standard(store) => store.get_many(keys, version),
261		}
262	}
263}
264
265impl MultiVersionStore for MultiStore {}