reifydb-store-multi 0.7.0

Multi-version storage for OLTP operations with MVCC support
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 ReifyDB

pub mod commit;
pub mod persistent;
pub mod read;

use std::{collections::HashMap, ops::Bound};

use reifydb_core::{common::CommitVersion, encoded::key::EncodedKey, interface::store::EntryKind};
use reifydb_value::{Result, util::cowvec::CowVec};

use crate::MultiVersionScope;

pub type TierBatch = HashMap<EntryKind, Vec<(EncodedKey, Option<CowVec<u8>>)>>;

#[derive(Debug, Clone)]
pub enum VersionedGetResult {
	Value {
		value: CowVec<u8>,
		version: CommitVersion,
	},

	Tombstone,

	NotFound,
}

impl VersionedGetResult {
	pub fn value(self) -> Option<CowVec<u8>> {
		match self {
			VersionedGetResult::Value {
				value,
				..
			} => Some(value),
			VersionedGetResult::Tombstone | VersionedGetResult::NotFound => None,
		}
	}
}

#[derive(Debug, Clone)]
pub struct RawEntry {
	pub key: EncodedKey,
	pub version: CommitVersion,
	pub value: Option<CowVec<u8>>,
}

#[derive(Debug, Clone)]
pub struct RangeBatch {
	pub entries: Vec<RawEntry>,

	pub has_more: bool,
}

impl RangeBatch {
	pub fn empty() -> Self {
		Self {
			entries: Vec::new(),
			has_more: false,
		}
	}

	pub fn is_empty(&self) -> bool {
		self.entries.is_empty()
	}
}

#[derive(Debug, Clone)]
pub struct RangeCursor {
	pub last_key: Option<EncodedKey>,

	pub exhausted: bool,
}

#[derive(Debug, Clone, Default)]
pub struct HistoricalCursor {
	pub last_key: Option<EncodedKey>,
	pub last_version: Option<CommitVersion>,
	pub exhausted: bool,
}

impl HistoricalCursor {
	pub fn new() -> Self {
		Self::default()
	}

	pub fn is_exhausted(&self) -> bool {
		self.exhausted
	}
}

impl RangeCursor {
	pub fn new() -> Self {
		Self {
			last_key: None,
			exhausted: false,
		}
	}

	pub fn is_exhausted(&self) -> bool {
		self.exhausted
	}
}

impl Default for RangeCursor {
	fn default() -> Self {
		Self::new()
	}
}

pub trait TierStorage: Send + Sync + Clone + 'static {
	fn get(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<VersionedGetResult>;

	fn get_many(
		&self,
		table: EntryKind,
		keys: &[&[u8]],
		version: CommitVersion,
	) -> Result<Vec<VersionedGetResult>> {
		let mut out = Vec::with_capacity(keys.len());
		for &key in keys {
			out.push(self.get(table, key, version)?);
		}
		Ok(out)
	}

	fn contains(&self, table: EntryKind, key: &[u8], version: CommitVersion) -> Result<bool> {
		Ok(matches!(self.get(table, key, version)?, VersionedGetResult::Value { .. }))
	}

	fn set(&self, version: CommitVersion, batches: TierBatch) -> Result<()>;

	fn range_next(
		&self,
		table: EntryKind,
		cursor: &mut RangeCursor,
		start: Bound<&[u8]>,
		end: Bound<&[u8]>,
		scope: MultiVersionScope,
		batch_size: usize,
	) -> Result<RangeBatch>;

	fn range_rev_next(
		&self,
		table: EntryKind,
		cursor: &mut RangeCursor,
		start: Bound<&[u8]>,
		end: Bound<&[u8]>,
		scope: MultiVersionScope,
		batch_size: usize,
	) -> Result<RangeBatch>;

	fn ensure_table(&self, table: EntryKind) -> Result<()>;

	fn clear_table(&self, table: EntryKind) -> Result<()>;

	fn drop(&self, batches: HashMap<EntryKind, Vec<(EncodedKey, CommitVersion)>>) -> Result<()>;

	fn get_all_versions(&self, table: EntryKind, key: &[u8]) -> Result<Vec<(CommitVersion, Option<CowVec<u8>>)>>;

	fn scan_historical_below(
		&self,
		table: EntryKind,
		cutoff: CommitVersion,
		cursor: &mut HistoricalCursor,
		batch_size: usize,
	) -> Result<Vec<(EncodedKey, CommitVersion)>>;
}

pub trait TierBackend: TierStorage {}