use std::sync::atomic::AtomicBool;
use ahash::{AHashMap, AHashSet};
use crate::common::counter::hardware_counter::HardwareCounterCell;
use crate::common::types::DeferredBehavior;
use crate::segment::common::operation_error::{OperationError, OperationResult};
use crate::segment::types::{Condition, Filter, PointIdType, SeqNumberType};
use crate::shard::segment_holder::{SegmentHolder, SegmentId};
pub(super) fn deferred_points_to_exclude_by_filter(
segments: &SegmentHolder,
per_segment_points: &AHashMap<SegmentId, Vec<PointIdType>>,
) -> AHashSet<PointIdType> {
let mut max_versions: AHashMap<PointIdType, Option<SeqNumberType>> = Default::default();
for (segment_id, point_ids) in per_segment_points {
let segment = segments.get(*segment_id).unwrap().get().read();
for point_id in point_ids {
let version = segment.point_version(*point_id);
let entry = max_versions.entry(*point_id).or_insert(None);
*entry = std::cmp::max(*entry, version);
}
}
let mut to_exclude = AHashSet::new();
for (_segment_id, segment) in segments.iter() {
let segment = segment.get().read();
if !segment.has_deferred_points() {
continue;
}
for (point_id, max_version) in &max_versions {
if segment.has_point(*point_id, DeferredBehavior::WithDeferred)
&& segment.point_version(*point_id) > *max_version
&& segment.point_is_deferred(*point_id)
{
to_exclude.insert(*point_id);
}
}
}
to_exclude
}
pub(crate) fn select_excluded_by_filter_ids(
segments: &SegmentHolder,
point_ids: impl IntoIterator<Item = PointIdType>,
filter: Filter,
hw_counter: &HardwareCounterCell,
) -> OperationResult<AHashSet<PointIdType>> {
let non_match_filter =
Filter::new_must_not(Condition::Filter(filter)).with_point_ids(point_ids);
Ok(points_by_filter(segments, &non_match_filter, hw_counter)?
.into_iter()
.collect())
}
pub(crate) fn points_by_filter(
segments: &SegmentHolder,
filter: &Filter,
hw_counter: &HardwareCounterCell,
) -> OperationResult<Vec<PointIdType>> {
let is_stopped = AtomicBool::new(false);
let mut has_deferred = false;
let per_segment_points: AHashMap<SegmentId, Vec<PointIdType>> = segments
.iter()
.map(|(segment_id, segment)| {
let segment = segment.get().read();
let point_ids = segment.read_filtered(
None,
None,
Some(filter),
&is_stopped,
hw_counter,
DeferredBehavior::WithDeferred,
)?;
has_deferred |= segment.has_deferred_points();
Ok((segment_id, point_ids))
})
.collect::<OperationResult<_>>()?;
let mut affected_points: Vec<PointIdType> = per_segment_points
.values()
.flat_map(|v| v.iter().copied())
.collect();
if has_deferred {
let to_exclude = deferred_points_to_exclude_by_filter(segments, &per_segment_points);
if !to_exclude.is_empty() {
affected_points.retain(|id| !to_exclude.contains(id));
}
}
Ok(affected_points)
}
pub(super) fn check_unprocessed_points(
points: &[PointIdType],
processed: &AHashSet<PointIdType>,
) -> OperationResult<usize> {
let first_missed_point = points.iter().copied().find(|p| !processed.contains(p));
match first_missed_point {
None => Ok(processed.len()),
Some(missed_point_id) => Err(OperationError::PointIdError { missed_point_id }),
}
}