use std::sync::Arc;
use crate::Position;
use crate::event::EventRef;
use crate::index::IndexSet;
use crate::log::set::SegmentSet;
use crate::query::{AppendCondition, Matches};
use super::tips::{StagedTips, TagTips, Verdict};
use super::{AppendError, ConflictSite};
pub fn evaluate(
cond: &AppendCondition,
main: &TagTips,
staged: &StagedTips,
index: &IndexSet,
set: &SegmentSet,
verify: bool,
force_scan: bool,
) -> Result<Option<ConflictSite>, AppendError> {
let query = &cond.fail_if_events_match;
if staged.may_conflict(query) {
return Ok(Some(ConflictSite::SameBatch));
}
match main.may_match(query, cond.after) {
Verdict::DefinitelyNoMatch if verify => {
Ok(verified_against_scan(None, set, cond)?.map(ConflictSite::Durable))
}
Verdict::DefinitelyNoMatch => Ok(None),
Verdict::Unknown if force_scan => Ok(scan_for_match(set, cond)?.map(ConflictSite::Durable)),
Verdict::Unknown => match index.find_match(query, cond.after) {
Ok(found) if verify => {
Ok(verified_against_scan(found, set, cond)?.map(ConflictSite::Durable))
}
Ok(found) => Ok(found.map(ConflictSite::Durable)),
Err(_err) => {
#[cfg(feature = "tracing")]
tracing::warn!(
"index existence check unavailable ({_err}); scanning the log for the condition range"
);
Ok(scan_for_match(set, cond)?.map(ConflictSite::Durable))
}
},
}
}
fn verified_against_scan(
fast: Option<Position>,
set: &SegmentSet,
cond: &AppendCondition,
) -> Result<Option<Position>, AppendError> {
let scanned = scan_for_match(set, cond)?;
if fast != scanned {
#[cfg(feature = "tracing")]
tracing::error!(
"verify: fast-path {fast:?} disagreed with scan oracle {scanned:?} for query {:?} after {}",
cond.fail_if_events_match,
cond.after,
);
debug_assert_eq!(
fast, scanned,
"verify: fast-path disagreed with the scan oracle"
);
}
Ok(scanned)
}
fn scan_for_match(
set: &SegmentSet,
cond: &AppendCondition,
) -> Result<Option<Position>, AppendError> {
let query = &cond.fail_if_events_match;
let mut scan = set.scan_after(cond.after);
while let Some(item) = scan.next() {
let record = item.map_err(|err| AppendError::Log(Arc::new(err)))?;
let event = EventRef::from_bytes(record.data).map_err(AppendError::Corrupt)?;
if query.matches(event) {
return Ok(Some(record.position));
}
}
Ok(None)
}