Skip to main content

reifydb_store_multi/tier/commit/
buffer.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{collections::HashMap, ops::Bound};
5
6use reifydb_codec::key::encoded::EncodedKey;
7use reifydb_core::{
8	common::CommitVersion,
9	interface::store::EntryKind,
10	metrics::{collect::MetricsCollector, sample::MetricsSample},
11};
12use reifydb_value::{Result, byte_size::ByteSize, util::cowvec::CowVec};
13
14use crate::{
15	MultiVersionScope,
16	tier::{
17		DisplacedValues, HistoricalCursor, RangeBatch, RangeCursor, TierBackend, TierBatch, TierStorage,
18		VersionedGetResult,
19		commit::memory::storage::{EvictedVersion, MemoryRowStorage},
20	},
21};
22
23#[derive(Clone)]
24#[repr(u8)]
25pub enum MultiCommitBufferTier {
26	Memory(MemoryRowStorage) = 0,
27}
28
29impl MultiCommitBufferTier {
30	pub fn memory() -> Self {
31		Self::Memory(MemoryRowStorage::new())
32	}
33}
34
35impl MultiCommitBufferTier {
36	pub fn maintenance(&self) {
37		match self {
38			Self::Memory(_) => {}
39		}
40	}
41
42	pub fn count_current(&self, table: EntryKind) -> Result<u64> {
43		match self {
44			Self::Memory(s) => s.count_current(table),
45		}
46	}
47
48	pub fn count_historical(&self, table: EntryKind) -> Result<u64> {
49		match self {
50			Self::Memory(s) => s.count_historical(table),
51		}
52	}
53
54	pub fn list_all_entry_kinds(&self) -> Result<Vec<EntryKind>> {
55		match self {
56			Self::Memory(s) => s.list_all_entry_kinds(),
57		}
58	}
59
60	pub fn list_entry_kinds_by_oldest_pending(&self) -> Result<Vec<EntryKind>> {
61		match self {
62			Self::Memory(s) => s.list_entry_kinds_by_oldest_pending(),
63		}
64	}
65
66	pub fn oldest_pending_for(&self, kind: EntryKind) -> Option<CommitVersion> {
67		match self {
68			Self::Memory(s) => s.oldest_pending_for(kind),
69		}
70	}
71
72	pub fn oldest_pending_version(&self) -> Option<CommitVersion> {
73		match self {
74			Self::Memory(s) => s.oldest_pending_version(),
75		}
76	}
77
78	pub fn current_resident_bytes(&self) -> ByteSize {
79		match self {
80			Self::Memory(s) => s.current_resident_bytes(),
81		}
82	}
83
84	pub fn historical_resident_bytes(&self) -> ByteSize {
85		match self {
86			Self::Memory(s) => s.historical_resident_bytes(),
87		}
88	}
89
90	#[inline]
91	pub fn compact(
92		&self,
93		batches: HashMap<EntryKind, Vec<(EncodedKey, CommitVersion)>>,
94	) -> Result<Vec<EvictedVersion>> {
95		match self {
96			Self::Memory(s) => s.compact(batches),
97		}
98	}
99
100	#[inline]
101	pub fn get_all_versions(
102		&self,
103		table: EntryKind,
104		key: &[u8],
105	) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>> {
106		match self {
107			Self::Memory(s) => s.get_all_versions(table, key),
108		}
109	}
110
111	#[inline]
112	pub fn scan_historical_below(
113		&self,
114		table: EntryKind,
115		cutoff: CommitVersion,
116		cursor: &mut HistoricalCursor,
117		batch_size: usize,
118	) -> Result<Vec<(EncodedKey, CommitVersion)>> {
119		match self {
120			Self::Memory(s) => s.scan_historical_below(table, cutoff, cursor, batch_size),
121		}
122	}
123}
124
125impl MetricsCollector for MultiCommitBufferTier {
126	fn collect(&self, out: &mut Vec<MetricsSample>) {
127		out.push(MetricsSample::heap("commit_buffer", "current_bytes", self.current_resident_bytes()));
128		out.push(MetricsSample::heap("commit_buffer", "historical_bytes", self.historical_resident_bytes()));
129		let kinds = self.list_all_entry_kinds().unwrap_or_default();
130		out.push(MetricsSample::count("commit_buffer", "table_count", kinds.len() as u64));
131		let current_entries: u64 = kinds.iter().map(|kind| self.count_current(*kind).unwrap_or(0)).sum();
132		out.push(MetricsSample::count("commit_buffer", "current_entries", current_entries));
133	}
134}
135
136impl TierStorage for MultiCommitBufferTier {
137	#[inline]
138	fn get(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<VersionedGetResult> {
139		match self {
140			Self::Memory(s) => s.get(table, key, version),
141		}
142	}
143
144	#[inline]
145	fn contains(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<bool> {
146		match self {
147			Self::Memory(s) => s.contains(table, key, version),
148		}
149	}
150
151	#[inline]
152	fn set(&self, version: CommitVersion, batches: TierBatch) -> Result<DisplacedValues> {
153		match self {
154			Self::Memory(s) => s.set(version, batches),
155		}
156	}
157
158	#[inline]
159	fn range_next(
160		&self,
161		table: EntryKind,
162		cursor: &mut RangeCursor,
163		start: Bound<&[u8]>,
164		end: Bound<&[u8]>,
165		scope: MultiVersionScope,
166		batch_size: usize,
167	) -> Result<RangeBatch> {
168		match self {
169			Self::Memory(s) => s.range_next(table, cursor, start, end, scope, batch_size),
170		}
171	}
172
173	#[inline]
174	fn range_rev_next(
175		&self,
176		table: EntryKind,
177		cursor: &mut RangeCursor,
178		start: Bound<&[u8]>,
179		end: Bound<&[u8]>,
180		scope: MultiVersionScope,
181		batch_size: usize,
182	) -> Result<RangeBatch> {
183		match self {
184			Self::Memory(s) => s.range_rev_next(table, cursor, start, end, scope, batch_size),
185		}
186	}
187
188	#[inline]
189	fn ensure_table(&self, table: EntryKind) -> Result<()> {
190		match self {
191			Self::Memory(s) => s.ensure_table(table),
192		}
193	}
194
195	#[inline]
196	fn clear_table(&self, table: EntryKind) -> Result<()> {
197		match self {
198			Self::Memory(s) => s.clear_table(table),
199		}
200	}
201}
202
203impl TierBackend for MultiCommitBufferTier {}
204
205#[cfg(test)]
206pub mod tests {
207	use super::*;
208
209	#[test]
210	fn test_memory_backend() {
211		let storage = MultiCommitBufferTier::memory();
212
213		let key = EncodedKey::new(b"key");
214		let version = CommitVersion(1);
215
216		storage.set(
217			version,
218			HashMap::from([(EntryKind::Multi, vec![(key.clone(), Some(CowVec::new(b"value".to_vec())))])]),
219		)
220		.unwrap();
221		assert_eq!(
222			storage.get(EntryKind::Multi, &key, version).unwrap().value().as_deref(),
223			Some(b"value".as_slice())
224		);
225	}
226
227	#[test]
228	fn test_range_next_memory() {
229		let storage = MultiCommitBufferTier::memory();
230
231		let version = CommitVersion(1);
232		storage.set(
233			version,
234			HashMap::from([(
235				EntryKind::Multi,
236				vec![
237					(EncodedKey::new(b"a"), Some(CowVec::new(b"1".to_vec()))),
238					(EncodedKey::new(b"b"), Some(CowVec::new(b"2".to_vec()))),
239					(EncodedKey::new(b"c"), Some(CowVec::new(b"3".to_vec()))),
240				],
241			)]),
242		)
243		.unwrap();
244
245		let mut cursor = RangeCursor::new();
246		let batch = storage
247			.range_next(
248				EntryKind::Multi,
249				&mut cursor,
250				Bound::Unbounded,
251				Bound::Unbounded,
252				MultiVersionScope::AsOf {
253					read: version,
254				},
255				100,
256			)
257			.unwrap();
258
259		assert_eq!(batch.entries.len(), 3);
260		assert!(!batch.has_more);
261		assert!(cursor.exhausted);
262	}
263}