use crate::read_view::{ReadLimits, ScanProjection};
use crate::resources::ByteReservation;
use crate::{Op, StorageError, StorageStatistics, VersionEntry};
use std::collections::BTreeMap;
use std::ops::Bound;
use std::path::Path;
use std::sync::Arc;
pub(crate) trait StorageReadView: Send + Sync {
fn visit_value(
&self,
key: &[u8],
ci: u64,
current: bool,
limits: ReadLimits,
projection: ScanProjection,
visit: &mut dyn FnMut(&[u8]),
) -> Result<bool, StorageError>;
fn read_value(
&self,
key: &[u8],
ci: u64,
current: bool,
limits: ReadLimits,
projection: ScanProjection,
) -> Result<Option<Vec<u8>>, StorageError> {
let mut value = None;
self.visit_value(key, ci, current, limits, projection, &mut |raw| {
value = Some(raw.to_vec());
})?;
Ok(value)
}
fn next_key(
&self,
lower: &Bound<Vec<u8>>,
upper: &Bound<Vec<u8>>,
limits: ReadLimits,
include_history: bool,
) -> Result<Option<Vec<u8>>, StorageError>;
fn current_range<'a>(
&'a self,
start: &[u8],
end: &[u8],
limits: ReadLimits,
projection: ScanProjection,
) -> Result<Box<dyn Iterator<Item = Result<(Vec<u8>, Vec<u8>), StorageError>> + 'a>, StorageError>;
fn native_range<'a>(
&'a self,
_lower: &Bound<Vec<u8>>,
_upper: &Bound<Vec<u8>>,
_limits: ReadLimits,
_projection: ScanProjection,
) -> Result<
Option<Box<dyn Iterator<Item = Result<(Vec<u8>, Vec<u8>), StorageError>> + 'a>>,
StorageError,
> {
Ok(None)
}
fn visible_ci(&self) -> Result<u64, StorageError>;
fn history_floor(&self) -> Result<u64, StorageError>;
fn snapshot_pinned(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct CommitOps<'a> {
pub(crate) ci: u64,
pub(crate) base: &'a [Op],
pub(crate) derived: &'a [Op],
}
impl<'a> CommitOps<'a> {
pub(crate) fn ops(&self) -> impl Iterator<Item = &'a Op> + Clone {
let (base, derived) = (self.base, self.derived);
base.iter().chain(derived)
}
}
#[derive(Debug, Default)]
pub struct LoadedState {
pub data: BTreeMap<Vec<u8>, Vec<u8>>,
pub created: BTreeMap<Vec<u8>, u64>,
pub history: BTreeMap<Vec<u8>, Vec<VersionEntry>>,
pub gc_watermark: u64,
}
pub(crate) const COMPACT_BUSY: &str = "compaction busy: read transactions open";
pub(crate) trait StorageBackend: Send + Sync {
fn load(&self) -> Result<LoadedState, String>;
fn load_watermark(&self) -> Result<(u64, u64), String> {
let state = self.load()?;
let lsn = match state.data.get(crate::LAST_APPLIED_KEY) {
Some(value) => crate::wal::decode_last_applied(value)
.map_err(|error| format!("corrupt last-applied watermark: {error}"))?,
None => 0,
};
Ok((lsn, state.gc_watermark))
}
fn apply(&self, ops: &[Op], commit_index: u64) -> Result<(), String>;
fn apply_many(&self, batches: &[CommitOps<'_>]) -> Result<(), String>;
fn retire_before(&self, watermark: u64) -> Result<(), String>;
fn retire_before_batch(
&self,
watermark: u64,
_cursor: Option<Vec<u8>>,
_max_keys: usize,
) -> Result<Option<Vec<u8>>, String> {
self.retire_before(watermark)?;
Ok(None)
}
fn begin_shutdown(&self) {}
fn checkpoint(&self, _durable_lsn: u64) -> Result<bool, String> {
Ok(false)
}
fn while_writes_paused(&self, run: &mut dyn FnMut()) -> Result<(), String> {
run();
Ok(())
}
fn compact(&self) -> Result<(), String>;
fn storage_stats(&self) -> Result<StorageStatistics, String> {
Err("backend does not provide storage statistics".into())
}
fn read_view(&self) -> Result<Box<dyn StorageReadView>, StorageError>;
fn lock_times(&self) -> crate::BackendLockTimes {
crate::BackendLockTimes::default()
}
fn writer_holds(&self) -> Vec<(crate::WriterHolder, crate::WriterHolds)> {
Vec::new()
}
}
pub(crate) fn open_backend(path: &Path, cache: ByteReservation) -> Result<BackendHandle, String> {
Ok(Arc::new(crate::redb_backend::RedbBackend::open(
path, cache,
)?))
}
pub(crate) type BackendHandle = Arc<dyn StorageBackend>;