use super::{FilterAnswer, FilterQuery, filter_answer, seqno_filter, table_reader};
use crate::{
InternalValue, SeqNo, UserKey, ValueType,
blob_tree::BlobSource,
key::InternalKey,
memtable::Memtable,
merge_operator::MergeOperator,
mvcc_stream::{MvccStream, ValueLog},
range_tombstone::RangeTombstone,
table::Table,
version::SuperVersion,
};
use alloc::sync::Arc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::ops::Bound;
#[derive(Clone, Copy)]
enum Source<'a> {
Memtable(&'a Memtable),
Table(&'a Table, (usize, usize, usize)),
}
pub struct MergeWalk<'v> {
super_version: &'v SuperVersion,
key: UserKey,
seqno: SeqNo,
sources: Vec<(SeqNo, usize, Source<'v>)>,
next: usize,
entries: Vec<(usize, InternalValue)>,
tombstones: Vec<(RangeTombstone, SeqNo)>,
floor: Option<SeqNo>,
pending: Option<(usize, usize, usize)>,
}
pub struct MergeTable<'v> {
pub table: &'v Table,
pub at: (usize, usize, usize),
}
impl<'v> MergeWalk<'v> {
pub(crate) fn new(super_version: &'v SuperVersion, key: &[u8], seqno: SeqNo) -> Self {
let comparator = super_version.active_memtable.comparator.as_ref();
let mut sources: Vec<(SeqNo, usize, Source<'v>)> = Vec::new();
for memtable in core::iter::once(&super_version.active_memtable)
.chain(super_version.sealed_memtables.iter().rev())
{
if let Some(highest) = memtable.get_highest_seqno() {
sources.push((highest, sources.len(), Source::Memtable(memtable)));
}
}
let bounds = (Bound::Included(key), Bound::Included(key));
for (level_idx, level) in super_version.version.iter_levels().enumerate() {
for (run_idx, run) in level.iter().enumerate() {
let Some((lo, hi)) =
run.range_overlap_indexes_cmp::<&[u8], _, _>(&bounds, comparator)
else {
continue;
};
for (pos, table) in run
.get(lo..=hi)
.unwrap_or_default()
.iter()
.enumerate()
.map(|(at, table)| (lo + at, table))
{
if table.check_key_range_overlap_cmp(&bounds, comparator) {
sources.push((
table.get_highest_seqno(),
sources.len(),
Source::Table(table, (level_idx, run_idx, pos)),
));
}
}
}
}
sources.sort_unstable_by(|a, b| b.0.cmp(&a.0).then(a.1.cmp(&b.1)));
Self {
super_version,
key: UserKey::from(key),
seqno,
sources,
next: 0,
entries: Vec::new(),
tombstones: Vec::new(),
floor: None,
pending: None,
}
}
pub(crate) fn next_table(&mut self) -> crate::Result<Option<MergeTable<'v>>> {
debug_assert!(
self.pending.is_none(),
"a table handed out was not taken back"
);
let super_version = self.super_version;
let comparator = super_version.active_memtable.comparator.as_ref();
let owned_key = self.key.clone();
let key = &owned_key[..];
while let Some(&(highest, rank, source)) = self.sources.get(self.next) {
if self.floor.is_some_and(|floor| highest < floor) {
self.next = self.sources.len();
break;
}
self.next += 1;
let (entries_before, tombstones_before) = (self.entries.len(), self.tombstones.len());
match source {
Source::Memtable(memtable) => {
let seqno = self.seqno;
memtable.for_each_range_tombstone_containing(key, |rt| {
self.tombstones.push((rt.clone(), seqno));
});
let range = (
Bound::Included(InternalKey::bound(
self.key.clone(),
SeqNo::MAX,
ValueType::Tombstone,
)),
Bound::Included(InternalKey::bound(self.key.clone(), 0, ValueType::Value)),
);
take_versions(
memtable
.range_internal(range)
.filter(|item| seqno_filter(item.key.seqno, seqno))
.map(Ok),
rank,
&mut self.entries,
)?;
self.raise_floor(entries_before, tombstones_before);
}
Source::Table(table, at) => {
let starting = table.range_tombstones().partition_point(|rt| {
comparator.compare(&rt.start, key) != core::cmp::Ordering::Greater
});
self.tombstones.extend(
table
.range_tombstones()
.iter()
.take(starting)
.filter(|rt| rt.contains_key_with(key, comparator))
.map(|rt| (rt.clone(), self.seqno)),
);
self.pending = Some((entries_before, tombstones_before, rank));
return Ok(Some(MergeTable { table, at }));
}
}
}
Ok(None)
}
pub(crate) fn take_table(
&mut self,
versions: impl Iterator<Item = crate::Result<InternalValue>>,
) -> crate::Result<()> {
let Some((entries_before, tombstones_before, rank)) = self.pending.take() else {
debug_assert!(false, "versions handed back for no table");
return Ok(());
};
take_versions(versions, rank, &mut self.entries)?;
self.raise_floor(entries_before, tombstones_before);
Ok(())
}
fn raise_floor(&mut self, entries_before: usize, tombstones_before: usize) {
let seqno = self.seqno;
let bases = self
.entries
.get(entries_before..)
.unwrap_or_default()
.iter()
.filter(|(_, entry)| !entry.key.value_type.is_merge_operand())
.map(|(_, entry)| entry.key.seqno);
let hiding = self
.tombstones
.get(tombstones_before..)
.unwrap_or_default()
.iter()
.filter(|(rt, _)| rt.visible_at(seqno))
.map(|(rt, _)| rt.seqno);
self.floor = self.floor.max(bases.chain(hiding).max());
}
pub(crate) fn finish(
self,
merge_operator: &Arc<dyn MergeOperator>,
blob_source: Option<&BlobSource>,
) -> crate::Result<Option<InternalValue>> {
let super_version = self.super_version;
self.into_read()
.finish(super_version, merge_operator, blob_source)
}
pub(crate) fn into_read(self) -> MergeRead {
MergeRead {
key: self.key,
entries: self.entries,
tombstones: self.tombstones,
}
}
}
pub struct MergeRead {
key: UserKey,
entries: Vec<(usize, InternalValue)>,
tombstones: Vec<(RangeTombstone, SeqNo)>,
}
impl MergeRead {
pub(crate) fn finish(
self,
super_version: &SuperVersion,
merge_operator: &Arc<dyn MergeOperator>,
blob_source: Option<&BlobSource>,
) -> crate::Result<Option<InternalValue>> {
let Self {
key,
mut entries,
tombstones,
} = self;
let comparator = super_version.active_memtable.comparator.as_ref();
entries.sort_by(|(a_rank, a), (b_rank, b)| {
b.key.seqno.cmp(&a.key.seqno).then(a_rank.cmp(b_rank))
});
let Some((_, head)) = entries.first() else {
return Ok(None);
};
if tombstones
.iter()
.any(|(rt, cutoff)| rt.should_suppress_with(&key, head.key.seqno, *cutoff, comparator))
{
return Ok(None);
}
let resolved = MvccStream::new_with_comparator(
entries.into_iter().map(|(_, entry)| Ok(entry)),
Some(Arc::clone(merge_operator)),
super_version.active_memtable.comparator.clone(),
)
.with_value_log(blob_source.map(|source| ValueLog {
source,
version: &super_version.version,
}))
.with_range_tombstones(tombstones)
.next()
.transpose()?;
Ok(resolved.filter(|value| !value.key.is_tombstone()))
}
}
#[derive(Clone, Copy)]
pub struct TableFilter(FilterAnswer);
impl TableFilter {
pub(crate) fn of(table: &Table, key: &[u8]) -> Self {
Self(filter_answer(
FilterQuery {
prefix_hash: None,
key_hash: Some(crate::hash::hash64(key)),
bloom_key: Some(key),
#[cfg(feature = "metrics")]
metrics: None,
},
table,
))
}
pub(crate) fn absent(self) -> bool {
self.0 == FilterAnswer::Absent
}
pub(crate) fn settle(self, table: &Table, found: bool) {
if !found && self.0.passed() && table.probe_stats().is_some() {
table.count_probes(crate::table::probe_stats::ProbeCounts {
probes: 0,
negatives: 1,
});
}
}
pub(crate) fn versions<'t>(
self,
table: &'t Table,
key: &[u8],
seqno: SeqNo,
) -> impl Iterator<Item = crate::Result<InternalValue>> + 't {
let reader = (!self.absent()).then(|| {
let key = UserKey::from(key);
table_reader(
self.0,
table,
(Bound::Included(key.clone()), Bound::Included(key)),
seqno,
)
});
reader.into_iter().flatten()
}
}
pub fn resolve_point_merge(
super_version: &SuperVersion,
key: &[u8],
seqno: SeqNo,
merge_operator: &Arc<dyn MergeOperator>,
blob_source: Option<&BlobSource>,
) -> crate::Result<Option<InternalValue>> {
let mut walk = MergeWalk::new(super_version, key, seqno);
while let Some(next) = walk.next_table()? {
let filter = TableFilter::of(next.table, key);
walk.take_table(filter.versions(next.table, key, seqno))?;
}
walk.finish(merge_operator, blob_source)
}
fn take_versions(
versions: impl Iterator<Item = crate::Result<InternalValue>>,
rank: usize,
entries: &mut Vec<(usize, InternalValue)>,
) -> crate::Result<()> {
for version in versions {
let version = version?;
let base = !version.key.value_type.is_merge_operand();
entries.push((rank, version));
if base {
break;
}
}
Ok(())
}
#[cfg(test)]
mod tests;