mod fingerprint;
mod last;
use std::cmp::Ordering;
use std::sync::Arc;
use uqa_core::memory::{BudgetedVec, MemoryReservation};
use crate::read_control::StorageReadControl;
use super::{
CommitSequence, PreparedRecordWrite, PrivateRecordSnapshot, RecordVersion, ScannedRecord,
SharedRecordValue, VersionResult,
};
#[derive(Clone, Copy)]
pub struct BorrowedRecord<'a> {
pub revision: Option<CommitSequence>,
pub value: Option<&'a [u8]>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RecordMetadata {
pub revision: Option<CommitSequence>,
pub live: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VisibleRecordRevision {
database: super::DatabaseId,
committed: Option<CommitSequence>,
private: Option<super::PrivateRecordRevision>,
}
impl VisibleRecordRevision {
pub(crate) fn committed(self, database: super::DatabaseId) -> Option<CommitSequence> {
(self.database == database && self.private.is_none())
.then_some(self.committed)
.flatten()
}
pub(crate) fn committed_sequence(self) -> Option<CommitSequence> {
self.committed
}
pub(crate) fn database(self) -> super::DatabaseId {
self.database
}
pub fn is_private(self) -> bool {
self.private.is_some()
}
}
impl From<BorrowedRecord<'_>> for RecordMetadata {
fn from(record: BorrowedRecord<'_>) -> Self {
Self {
revision: record.revision,
live: record.value.is_some(),
}
}
}
pub type RecordValueVisitor<'a> = dyn FnMut(Option<BorrowedRecord<'_>>) -> VersionResult<()> + 'a;
pub type RecordPointVisitor<'a> =
dyn FnMut(&[u8], Option<BorrowedRecord<'_>>) -> VersionResult<bool> + 'a;
pub type RecordKeyIterator<'a> = dyn Iterator<Item = VersionResult<BudgetedVec<u8>>> + 'a;
pub type RecordScanVisitor<'a> = dyn FnMut(&[u8], BorrowedRecord<'_>) -> VersionResult<bool> + 'a;
pub type RecordKeyVisitor<'a> = dyn FnMut(&[u8], RecordMetadata) -> VersionResult<bool> + 'a;
pub trait CommittedRecordSnapshot: Send + Sync {
fn sequence(&self) -> CommitSequence;
fn reclamation_epoch(&self) -> Option<u64> {
None
}
fn provider_snapshot(&self) -> Option<&dyn std::any::Any> {
None
}
fn commit_monitor(&self) -> Option<u64> {
None
}
fn adopt_commit_monitor(&self, _monitor: u64) -> bool {
false
}
fn metadata(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<RecordMetadata>> {
let mut found = None;
self.visit_value(key, control, &mut |record| {
found = record.map(Into::into);
Ok(())
})?;
Ok(found)
}
fn visit_keys(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
visit: &mut RecordKeyVisitor<'_>,
) -> VersionResult<()> {
self.visit_prefix(prefix, after, limit, control, &mut |key, record| {
visit(key, record.into())
})
}
fn visit_last_key(
&self,
prefix: &[u8],
before: Option<&[u8]>,
control: &StorageReadControl,
visit: &mut RecordKeyVisitor<'_>,
) -> VersionResult<()> {
last::by_scan(self, prefix, before, control, visit)
}
fn get(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<RecordVersion<SharedRecordValue>>>;
fn scan(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
) -> VersionResult<BudgetedVec<ScannedRecord>>;
fn visit_value(
&self,
key: &[u8],
control: &StorageReadControl,
visit: &mut RecordValueVisitor<'_>,
) -> VersionResult<()> {
let record = self.get(key, control)?;
visit(record.as_ref().map(|record| BorrowedRecord {
revision: Some(record.sequence()),
value: record.value().map(|value| &***value),
}))?;
control.cancellation().check()?;
Ok(())
}
fn visit_value_bounded(
&self,
_key: &[u8],
_max_bytes: usize,
control: &StorageReadControl,
_visit: &mut RecordValueVisitor<'_>,
) -> VersionResult<()> {
control.check()?;
Err(crate::StorageBackendError::Other(
"size-bounded committed record reads are not supported".into(),
)
.into())
}
fn visit_values(
&self,
keys: &mut RecordKeyIterator<'_>,
control: &StorageReadControl,
visit: &mut RecordPointVisitor<'_>,
) -> VersionResult<()> {
loop {
control.check()?;
let Some(key) = keys.next() else {
return Ok(());
};
let key = key?;
let mut more = true;
self.visit_value(&key, control, &mut |record| {
more = visit(&key, record)?;
Ok(())
})?;
control.check()?;
if !more {
return Ok(());
}
}
}
fn visit_prefix(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
visit: &mut RecordScanVisitor<'_>,
) -> VersionResult<()> {
control.cancellation().check()?;
let mut cursor: Option<BudgetedVec<u8>> = None;
let mut remaining = limit;
while remaining != 0 {
let mut page = self.scan(
prefix,
cursor.as_deref().or(after),
remaining.min(64),
control,
)?;
if page.is_empty() {
break;
}
for record in page.iter() {
control.cancellation().check()?;
if !visit(
&record.key,
BorrowedRecord {
revision: Some(record.version.sequence()),
value: record.version.value().map(|value| &***value),
},
)? {
control.cancellation().check()?;
return Ok(());
}
remaining -= 1;
}
cursor = page.pop().map(|record| record.key);
}
control.cancellation().check()?;
Ok(())
}
}
struct RetainedSnapshot<T> {
snapshot: T,
_memory: MemoryReservation,
}
pub fn retain_record_snapshot<T: CommittedRecordSnapshot + 'static>(
snapshot: T,
control: &StorageReadControl,
) -> VersionResult<Arc<dyn CommittedRecordSnapshot>> {
control.cancellation().check()?;
let memory = control
.memory()
.reserve(std::mem::size_of::<RetainedSnapshot<T>>())?;
Ok(Arc::new(RetainedSnapshot {
snapshot,
_memory: memory,
}))
}
impl<T: CommittedRecordSnapshot> CommittedRecordSnapshot for RetainedSnapshot<T> {
fn sequence(&self) -> CommitSequence {
self.snapshot.sequence()
}
fn reclamation_epoch(&self) -> Option<u64> {
self.snapshot.reclamation_epoch()
}
fn provider_snapshot(&self) -> Option<&dyn std::any::Any> {
self.snapshot.provider_snapshot()
}
fn commit_monitor(&self) -> Option<u64> {
self.snapshot.commit_monitor()
}
fn adopt_commit_monitor(&self, monitor: u64) -> bool {
self.snapshot.adopt_commit_monitor(monitor)
}
fn visit_last_key(
&self,
prefix: &[u8],
before: Option<&[u8]>,
control: &StorageReadControl,
visit: &mut RecordKeyVisitor<'_>,
) -> VersionResult<()> {
self.snapshot.visit_last_key(prefix, before, control, visit)
}
fn metadata(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<RecordMetadata>> {
self.snapshot.metadata(key, control)
}
fn visit_keys(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
visit: &mut RecordKeyVisitor<'_>,
) -> VersionResult<()> {
self.snapshot
.visit_keys(prefix, after, limit, control, visit)
}
fn get(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<RecordVersion<SharedRecordValue>>> {
self.snapshot.get(key, control)
}
fn scan(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
) -> VersionResult<BudgetedVec<ScannedRecord>> {
self.snapshot.scan(prefix, after, limit, control)
}
fn visit_value(
&self,
key: &[u8],
control: &StorageReadControl,
visit: &mut RecordValueVisitor<'_>,
) -> VersionResult<()> {
self.snapshot.visit_value(key, control, visit)
}
fn visit_values(
&self,
keys: &mut RecordKeyIterator<'_>,
control: &StorageReadControl,
visit: &mut RecordPointVisitor<'_>,
) -> VersionResult<()> {
self.snapshot.visit_values(keys, control, visit)
}
fn visit_value_bounded(
&self,
key: &[u8],
max_bytes: usize,
control: &StorageReadControl,
visit: &mut RecordValueVisitor<'_>,
) -> VersionResult<()> {
self.snapshot
.visit_value_bounded(key, max_bytes, control, visit)
}
fn visit_prefix(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
visit: &mut RecordScanVisitor<'_>,
) -> VersionResult<()> {
self.snapshot
.visit_prefix(prefix, after, limit, control, visit)
}
}
pub struct VisibleRecord {
original: Option<CommitSequence>,
value: Option<SharedRecordValue>,
private: bool,
}
impl VisibleRecord {
pub fn original_revision(&self) -> Option<CommitSequence> {
self.original
}
pub fn value(&self) -> Option<&[u8]> {
self.value.as_ref().map(|value| &***value)
}
pub fn is_private(&self) -> bool {
self.private
}
fn committed(version: RecordVersion<SharedRecordValue>) -> Self {
let (sequence, value) = version.into_parts();
Self {
original: Some(sequence),
value,
private: false,
}
}
fn private(write: &PreparedRecordWrite) -> Self {
Self {
original: write.expected(),
value: write.shared_value(),
private: true,
}
}
}
pub struct ScannedVisibleRecord {
pub key: BudgetedVec<u8>,
pub record: VisibleRecord,
}
pub struct MergedRecordSnapshot {
committed: Arc<dyn CommittedRecordSnapshot>,
private: PrivateRecordSnapshot,
serializable: Option<super::SerializableReadContext>,
}
impl MergedRecordSnapshot {
pub(in crate::mvcc) fn retained_source(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<Arc<dyn crate::key_value::KeyValueRead + Send + Sync>>> {
self.private.retained_source(key, control)
}
pub fn record_revision(
&self,
database: super::DatabaseId,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<VisibleRecordRevision>> {
control.check()?;
let Some(metadata) = self.metadata(key, control)?.filter(|record| record.live) else {
control.check()?;
return Ok(None);
};
let private = self.private_keys(key, None, 1, control)?;
let private = private
.first()
.filter(|record| record.key() == key)
.map(super::PrivateRecordKey::revision);
control.check()?;
Ok(Some(VisibleRecordRevision {
database,
committed: metadata.revision,
private,
}))
}
pub fn try_clone(&self) -> VersionResult<Self> {
Ok(Self {
committed: Arc::clone(&self.committed),
private: self.private.try_clone()?,
serializable: self.serializable.clone(),
})
}
pub fn new(
committed: Arc<dyn CommittedRecordSnapshot>,
private: PrivateRecordSnapshot,
) -> Self {
Self {
committed,
private,
serializable: None,
}
}
pub(crate) fn with_serializable(
mut self,
context: Option<super::SerializableReadContext>,
) -> Self {
self.serializable = context;
self
}
pub fn serializable(&self) -> Option<&super::SerializableReadContext> {
self.serializable.as_ref()
}
pub fn sequence(&self) -> CommitSequence {
self.committed.sequence()
}
pub fn private_revision(&self) -> Option<super::PrivateRecordRevision> {
self.private.revision()
}
pub fn committed(&self) -> &dyn CommittedRecordSnapshot {
self.committed.as_ref()
}
pub(super) fn retain_committed(&self) -> Arc<dyn CommittedRecordSnapshot> {
Arc::clone(&self.committed)
}
pub fn private_keys(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
) -> VersionResult<BudgetedVec<super::PrivateRecordKey>> {
self.private.scan_keys(prefix, after, limit, control)
}
pub fn visit_value(
&self,
key: &[u8],
control: &StorageReadControl,
visit: &mut RecordValueVisitor<'_>,
) -> VersionResult<()> {
if let Some(write) = self.private.get(key, control)? {
visit(Some(BorrowedRecord {
revision: write.expected(),
value: write.value(),
}))?;
control.cancellation().check()?;
return Ok(());
}
self.committed.visit_value(key, control, visit)
}
pub fn visit_values(
&self,
keys: &mut RecordKeyIterator<'_>,
control: &StorageReadControl,
visit: &mut RecordPointVisitor<'_>,
) -> VersionResult<()> {
if self.private_revision().is_none() {
return self.committed.visit_values(keys, control, visit);
}
loop {
control.check()?;
let Some(key) = keys.next() else {
return Ok(());
};
let key = key?;
let mut more = true;
self.visit_value(&key, control, &mut |record| {
more = visit(&key, record)?;
Ok(())
})?;
control.check()?;
if !more {
return Ok(());
}
}
}
pub fn visit_value_bounded(
&self,
key: &[u8],
max_bytes: usize,
control: &StorageReadControl,
visit: &mut RecordValueVisitor<'_>,
) -> VersionResult<()> {
control.check()?;
if let Some(write) = self.private.get(key, control)? {
if let Some(value) = write.value() {
control.check_value_size(value.len(), max_bytes)?;
}
visit(Some(BorrowedRecord {
revision: write.expected(),
value: write.value(),
}))?;
control.check()?;
return Ok(());
}
self.committed
.visit_value_bounded(key, max_bytes, control, visit)
}
pub fn visit_prefix(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
visit: &mut RecordScanVisitor<'_>,
) -> VersionResult<()> {
self.private.visit_merged::<super::projection::Values>(
&*self.committed,
prefix,
after,
limit,
control,
visit,
)
}
pub fn metadata(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<RecordMetadata>> {
if let Some(write) = self.private.get(key, control)? {
return Ok(Some(RecordMetadata {
revision: write.expected(),
live: write.value().is_some(),
}));
}
self.committed.metadata(key, control)
}
pub fn visit_keys(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
visit: &mut RecordKeyVisitor<'_>,
) -> VersionResult<()> {
self.private.visit_merged::<super::projection::Keys>(
&*self.committed,
prefix,
after,
limit,
control,
visit,
)
}
pub fn has_private_change(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<bool> {
Ok(self.private.get(key, control)?.is_some())
}
pub fn get(
&self,
key: &[u8],
control: &StorageReadControl,
) -> VersionResult<Option<VisibleRecord>> {
if let Some(write) = self.private.get(key, control)? {
return Ok(Some(VisibleRecord::private(&write)));
}
Ok(self
.committed
.get(key, control)?
.map(VisibleRecord::committed))
}
pub fn scan(
&self,
prefix: &[u8],
after: Option<&[u8]>,
limit: usize,
control: &StorageReadControl,
) -> VersionResult<BudgetedVec<ScannedVisibleRecord>> {
control.cancellation().check()?;
let mut result = BudgetedVec::new(control.memory());
if limit == 0 {
return Ok(result);
}
let (committed, _committed_memory) = self
.committed
.scan(prefix, after, limit, control)?
.into_parts();
let (private, _private_memory) = self
.private
.scan(prefix, after, limit, control)?
.into_parts();
let mut committed = committed.into_iter().peekable();
let mut private = private.into_iter().peekable();
while result.len() < limit {
control.cancellation().check()?;
let order = match (committed.peek(), private.peek()) {
(Some(committed), Some(private)) => committed.key.as_ref().cmp(private.key()),
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(None, None) => break,
};
let record = match order {
Ordering::Less => {
let committed = committed.next().expect("peeked committed record");
ScannedVisibleRecord {
key: committed.key,
record: VisibleRecord::committed(committed.version),
}
}
Ordering::Equal => {
let committed = committed.next().expect("peeked committed record");
let private = private.next().expect("peeked private record");
ScannedVisibleRecord {
key: committed.key,
record: VisibleRecord::private(&private),
}
}
Ordering::Greater => {
let private = private.next().expect("peeked private record");
let mut key = BudgetedVec::new(control.memory());
key.extend_from_slice(private.key())?;
ScannedVisibleRecord {
key,
record: VisibleRecord::private(&private),
}
}
};
result.push(record)?;
}
Ok(result)
}
}