use crate::file_group::reader_v2::reader_context::ReaderContext;
use crate::file_group::reader_v2::reader_parameters::ReaderParameters;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub(crate) enum ComparedWith {
Jvm,
VersionOne,
Both,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Gap {
PositionBasedMergeDeclined,
PositionBasedMergeAbandonedMidScan,
}
impl Gap {
pub(crate) fn compared_with(&self) -> ComparedWith {
match self {
Gap::PositionBasedMergeDeclined => ComparedWith::Jvm,
Gap::PositionBasedMergeAbandonedMidScan => ComparedWith::Jvm,
}
}
pub(crate) fn describe(&self) -> &'static str {
match self {
Gap::PositionBasedMergeDeclined => {
"this read asked to merge by record position but the base file's instant or \
format could not support it; merging by record key instead, which differs only \
where a file group holds duplicate keys"
}
Gap::PositionBasedMergeAbandonedMidScan => {
"this read began merging by record position and stopped partway through, because \
a log block's positions were not usable; the rest of the scan merged by record \
key, which differs only where a file group holds duplicate keys"
}
}
}
}
pub(crate) fn report_for_read(
context: &ReaderContext,
parameters: &ReaderParameters,
chose_position_merge: bool,
still_merging_by_position: bool,
) {
for gap in applicable(
context,
parameters,
chose_position_merge,
still_merging_by_position,
) {
log::warn!(
"{} (differs from: {:?})",
gap.describe(),
gap.compared_with()
);
}
}
fn applicable(
context: &ReaderContext,
parameters: &ReaderParameters,
chose_position_merge: bool,
still_merging_by_position: bool,
) -> Vec<Gap> {
let mut gaps = Vec::new();
let asked = parameters.use_record_position || context.should_merge_use_record_position;
if !asked || !context.has_log_files {
return gaps;
}
if !chose_position_merge {
gaps.push(Gap::PositionBasedMergeDeclined);
} else if !still_merging_by_position {
gaps.push(Gap::PositionBasedMergeAbandonedMidScan);
}
gaps
}
#[cfg(test)]
mod tests {
use super::*;
fn mor_context() -> ReaderContext {
let mut context = ReaderContext::empty();
context.has_log_files = true;
context
}
fn asked_for_positions() -> ReaderParameters {
ReaderParameters {
use_record_position: true,
..Default::default()
}
}
#[test]
fn test_an_ordinary_read_has_no_gaps() {
let gaps = applicable(&mor_context(), &ReaderParameters::default(), false, false);
assert!(gaps.is_empty());
}
#[test]
fn test_merging_by_position_is_not_reported() {
let gaps = applicable(&mor_context(), &asked_for_positions(), true, true);
assert!(gaps.is_empty());
}
#[test]
fn test_declining_to_merge_by_position_is_reported() {
let gaps = applicable(&mor_context(), &asked_for_positions(), false, false);
assert_eq!(gaps, vec![Gap::PositionBasedMergeDeclined]);
assert_eq!(gaps[0].compared_with(), ComparedWith::Jvm);
assert!(gaps[0].describe().contains("record position"));
}
#[test]
fn test_a_context_asking_for_position_merge_is_reported() {
let mut context = mor_context();
context.should_merge_use_record_position = true;
let gaps = applicable(&context, &ReaderParameters::default(), false, false);
assert_eq!(gaps, vec![Gap::PositionBasedMergeDeclined]);
}
#[test]
fn test_abandoning_position_merge_mid_scan_is_reported() {
let gaps = applicable(&mor_context(), &asked_for_positions(), true, false);
assert_eq!(gaps, vec![Gap::PositionBasedMergeAbandonedMidScan]);
assert!(
gaps[0].describe().contains("partway"),
"the description must distinguish this from declining up front, got: {}",
gaps[0].describe()
);
}
#[test]
fn test_a_slice_with_no_log_files_is_not_reported() {
let gaps = applicable(
&ReaderContext::empty(),
&asked_for_positions(),
false,
false,
);
assert!(gaps.is_empty());
}
#[test]
fn test_report_for_read_walks_every_applicable_gap() {
report_for_read(&mor_context(), &asked_for_positions(), true, false);
report_for_read(&mor_context(), &ReaderParameters::default(), false, false);
}
}