use windex::CandidateAddresses;
use super::LogCompactor;
use crate::{
error::Result,
host::{CompactSession, CompactStore},
};
pub(super) struct LatestRecord {
pub(super) main_addr: u64,
pub(super) index_addr: u64,
pub(super) is_tombstone: bool,
}
impl<S: CompactStore> LogCompactor<S> {
pub(super) async fn find_latest_address(
&self,
session: &S::Session,
key: &[u8],
known_curr: Option<(u64, bool)>,
) -> Result<Option<LatestRecord>> {
let mut addrs = {
let _guard = session.enter_epoch();
self.store.index().lookup_candidates(key)
};
addrs.sort_descending();
let begin_addr = self.store.begin_address();
let mut found = None;
let mut duplicates = CandidateAddresses::new();
for &addr in addrs.iter() {
let main_addr = if self.store.is_read_cache_addr(addr) {
self.store.skip_read_cache(addr)
} else {
addr
};
if main_addr == 0 || main_addr < begin_addr {
duplicates.push(addr);
continue;
}
if let Some((known_addr, known_tombstone)) = known_curr
&& main_addr == known_addr
{
if found.is_none() {
found = Some(LatestRecord {
main_addr,
index_addr: addr,
is_tombstone: known_tombstone,
});
} else {
duplicates.push(addr);
}
continue;
}
let record = if self.store.hlog().is_on_disk(main_addr) {
self.store.hlog().read_disk_record(main_addr).await
} else {
let _guard = session.enter_epoch();
self.store.hlog().read_record(main_addr).await
};
match record {
Ok(record) => {
match record.key() {
Ok(rec_key) if rec_key == key => {
if found.is_none() {
let is_tombstone = record.is_tombstone().unwrap_or(false);
found = Some(LatestRecord {
main_addr,
index_addr: addr,
is_tombstone,
});
} else {
duplicates.push(addr);
}
}
Err(e) => {
log::warn!(
"紧缩探针:候选 {main_addr:#x} 键解码失败({e}),本轮弃迁,存活记录可能丢失可见性"
);
}
Ok(_) => {}
}
}
Err(e) => {
if main_addr < self.store.begin_address() {
duplicates.push(addr);
continue;
}
return Err(e.into());
}
}
}
for &stale_addr in duplicates.iter() {
self.store.index().delete(key, stale_addr);
}
Ok(found)
}
}