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, tiered as in-memory commit buffer over a pluggable
5//! persistent tier. Invariant: a row at `version V` is what a reader whose snapshot is `>= V` sees when no
6//! later version exists at `V' <= snapshot`, and a commit must publish all its deltas atomically to readers.
7
8#![cfg_attr(not(debug_assertions), deny(clippy::disallowed_methods))]
9#![cfg_attr(debug_assertions, warn(clippy::disallowed_methods))]
10#![cfg_attr(not(debug_assertions), deny(warnings))]
11#![allow(clippy::tabs_in_doc_comments)]
12
13use reifydb_core::{
14	event::EventBus,
15	interface::version::{ComponentType, HasVersion, SystemVersion},
16};
17use reifydb_value::Result;
18
19pub mod flush;
20pub mod tier;
21
22pub mod config;
23pub mod store;
24
25use std::{collections::HashMap, sync::Arc};
26
27use config::{CommitBufferConfig, MultiStoreConfig};
28use reifydb_codec::key::encoded::{EncodedKey, EncodedKeyRange};
29use reifydb_core::{
30	common::CommitVersion,
31	delta::Delta,
32	interface::store::{
33		MultiVersionCommit, MultiVersionContains, MultiVersionGet, MultiVersionGetPrevious, MultiVersionRow,
34		MultiVersionStore,
35	},
36	metrics::collect::MetricsCollector,
37};
38use reifydb_runtime::shutdown::Shutdown;
39#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
40use reifydb_sqlite::SqliteTempPathGuard;
41use reifydb_value::util::cowvec::CowVec;
42use store::StandardMultiStore;
43use tier::read::ReadBufferShardMetrics;
44
45pub mod memory {}
46pub mod sqlite {}
47
48pub struct MultiStoreVersion;
49
50impl HasVersion for MultiStoreVersion {
51	fn version(&self) -> SystemVersion {
52		SystemVersion {
53			name: env!("CARGO_PKG_NAME")
54				.strip_prefix("reifydb-")
55				.unwrap_or(env!("CARGO_PKG_NAME"))
56				.to_string(),
57			version: env!("CARGO_PKG_VERSION").to_string(),
58			description: "Multi-version storage for OLTP operations with MVCC support".to_string(),
59			r#type: ComponentType::Module,
60		}
61	}
62}
63
64#[repr(u8)]
65#[derive(Clone)]
66pub enum MultiStore {
67	Standard(StandardMultiStore) = 0,
68}
69
70impl MultiStore {
71	pub fn standard(config: MultiStoreConfig) -> Self {
72		Self::Standard(StandardMultiStore::new(config).unwrap())
73	}
74}
75
76impl MultiStore {
77	pub fn testing_memory() -> Self {
78		MultiStore::Standard(StandardMultiStore::testing_memory())
79	}
80
81	pub fn testing_memory_with_eventbus(event_bus: EventBus) -> Self {
82		MultiStore::Standard(StandardMultiStore::testing_memory_with_eventbus(event_bus))
83	}
84
85	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
86	pub fn testing_memory_with_persistent_sqlite() -> (Self, SqliteTempPathGuard) {
87		let (store, guard) = StandardMultiStore::testing_memory_with_persistent_sqlite();
88		(MultiStore::Standard(store), guard)
89	}
90
91	#[cfg(all(feature = "sqlite", not(target_arch = "wasm32")))]
92	pub fn testing_memory_with_persistent_sqlite_with_eventbus(event_bus: EventBus) -> (Self, SqliteTempPathGuard) {
93		let (store, guard) = StandardMultiStore::testing_memory_with_persistent_sqlite_with_eventbus(event_bus);
94		(MultiStore::Standard(store), guard)
95	}
96
97	pub fn flush_pending_blocking(&self) {
98		match self {
99			MultiStore::Standard(store) => store.flush_pending_blocking(),
100		}
101	}
102
103	pub fn flush_all_blocking(&self) {
104		match self {
105			MultiStore::Standard(store) => store.flush_all_blocking(),
106		}
107	}
108
109	pub fn commit(&self) -> &tier::commit::buffer::MultiCommitBufferTier {
110		match self {
111			MultiStore::Standard(store) => store.commit(),
112		}
113	}
114
115	pub fn metrics_collectors(&self) -> Vec<Arc<dyn MetricsCollector>> {
116		match self {
117			MultiStore::Standard(store) => store.metrics_collectors(),
118		}
119	}
120
121	pub fn read_buffer_shard_metrics(&self) -> Vec<ReadBufferShardMetrics> {
122		match self {
123			MultiStore::Standard(store) => store.read_buffer_shard_metrics(),
124		}
125	}
126
127	pub fn persistent(&self) -> Option<&tier::persistent::MultiPersistentTier> {
128		match self {
129			MultiStore::Standard(store) => store.persistent(),
130		}
131	}
132
133	pub fn clear_eviction_watermark(&self) {
134		match self {
135			MultiStore::Standard(store) => store.clear_eviction_watermark(),
136		}
137	}
138}
139
140impl Shutdown for MultiStore {
141	fn shutdown(&self) {
142		match self {
143			MultiStore::Standard(store) => store.shutdown(),
144		}
145	}
146}
147
148impl MultiVersionGet for MultiStore {
149	#[inline]
150	fn get(&self, key: &EncodedKey, version: CommitVersion) -> Result<Option<MultiVersionRow>> {
151		match self {
152			MultiStore::Standard(store) => MultiVersionGet::get(store, key, version),
153		}
154	}
155}
156
157impl MultiVersionContains for MultiStore {
158	#[inline]
159	fn contains(&self, key: &EncodedKey, version: CommitVersion) -> Result<bool> {
160		match self {
161			MultiStore::Standard(store) => MultiVersionContains::contains(store, key, version),
162		}
163	}
164}
165
166impl MultiVersionCommit for MultiStore {
167	#[inline]
168	fn commit(&self, deltas: CowVec<Delta>, version: CommitVersion) -> Result<()> {
169		match self {
170			MultiStore::Standard(store) => MultiVersionCommit::commit(store, deltas, version),
171		}
172	}
173}
174
175impl MultiVersionGetPrevious for MultiStore {
176	#[inline]
177	fn get_previous_version(
178		&self,
179		key: &EncodedKey,
180		before_version: CommitVersion,
181	) -> Result<Option<MultiVersionRow>> {
182		match self {
183			MultiStore::Standard(store) => store.get_previous_version(key, before_version),
184		}
185	}
186}
187
188pub type MultiVersionRangeIterator<'a> = Box<dyn Iterator<Item = Result<MultiVersionRow>> + Send + 'a>;
189
190/// Selects which version each key resolves to during a range walk.
191#[derive(Debug, Copy, Clone, PartialEq, Eq)]
192pub enum MultiVersionScope {
193	/// For each key, yield the highest version `v` with `v <= read`.
194	AsOf {
195		read: CommitVersion,
196	},
197	/// For each key, yield the highest version `v` with `after < v <= read`.
198	/// Keys with no qualifying version are dropped from the output.
199	Between {
200		after: CommitVersion,
201		read: CommitVersion,
202	},
203}
204
205impl MultiVersionScope {
206	#[inline]
207	pub fn read(&self) -> CommitVersion {
208		match self {
209			Self::AsOf {
210				read,
211			}
212			| Self::Between {
213				read,
214				..
215			} => *read,
216		}
217	}
218
219	#[inline]
220	pub fn contains(&self, v: CommitVersion) -> bool {
221		match self {
222			Self::AsOf {
223				read,
224			} => v <= *read,
225			Self::Between {
226				after,
227				read,
228			} => v > *after && v <= *read,
229		}
230	}
231}
232
233impl MultiStore {
234	pub fn range(
235		&self,
236		range: EncodedKeyRange,
237		scope: MultiVersionScope,
238		batch_size: usize,
239	) -> MultiVersionRangeIterator<'_> {
240		match self {
241			MultiStore::Standard(store) => Box::new(store.range(range, scope, batch_size)),
242		}
243	}
244
245	pub fn range_persistence(
246		&self,
247		range: EncodedKeyRange,
248		scope: MultiVersionScope,
249		batch_size: usize,
250	) -> MultiVersionRangeIterator<'_> {
251		match self {
252			MultiStore::Standard(store) => Box::new(store.range_persistence(range, scope, batch_size)),
253		}
254	}
255
256	pub fn range_rev(
257		&self,
258		range: EncodedKeyRange,
259		scope: MultiVersionScope,
260		batch_size: usize,
261	) -> MultiVersionRangeIterator<'_> {
262		match self {
263			MultiStore::Standard(store) => Box::new(store.range_rev(range, scope, batch_size)),
264		}
265	}
266
267	pub fn range_rev_persistence(
268		&self,
269		range: EncodedKeyRange,
270		scope: MultiVersionScope,
271		batch_size: usize,
272	) -> MultiVersionRangeIterator<'_> {
273		match self {
274			MultiStore::Standard(store) => Box::new(store.range_rev_persistence(range, scope, batch_size)),
275		}
276	}
277
278	pub fn get_many(
279		&self,
280		keys: &[EncodedKey],
281		version: CommitVersion,
282	) -> Result<HashMap<EncodedKey, MultiVersionRow>> {
283		match self {
284			MultiStore::Standard(store) => store.get_many(keys, version),
285		}
286	}
287}
288
289impl MultiVersionStore for MultiStore {}