use gxhash::HashMap;
#[derive(Default)]
pub struct HybridLogScanMetrics {
scan_metrics: Vec<RegionMetrics>,
}
#[derive(Default)]
struct RegionMetrics {
region: String,
states: Vec<(String, (i64, i64))>,
index: HashMap<String, usize>,
}
impl HybridLogScanMetrics {
pub fn add_scan_metric(&mut self, region: &str, state: &str, size: i64) {
let region_idx = match self.scan_metrics.iter().position(|r| r.region == region) {
Some(idx) => idx,
None => {
self.scan_metrics.push(RegionMetrics {
region: region.into(),
states: Vec::new(),
index: HashMap::with_hasher(gxhash::GxBuildHasher::default()),
});
self.scan_metrics.len() - 1
}
};
let region_metrics = &mut self.scan_metrics[region_idx];
match region_metrics.index.get(state).copied() {
Some(state_idx) => {
let entry = &mut region_metrics.states[state_idx].1;
entry.0 += 1;
entry.1 += size;
}
None => {
region_metrics
.index
.insert(state.into(), region_metrics.states.len());
region_metrics.states.push((state.into(), (1, size)));
}
}
}
pub fn dump_scan_metrics_info(&self) -> String {
if self.scan_metrics.is_empty() {
return String::new();
}
let mut out = String::from("\n");
for region in &self.scan_metrics {
out.push_str("# Region: ");
out.push_str(®ion.region);
out.push('\n');
for (state, (count, size)) in ®ion.states {
out.push_str(&format!(" State: {state}, Count: {count}, Size: {size}\n"));
}
}
out
}
}
#[cfg(test)]
mod tests {
use super::HybridLogScanMetrics;
#[test]
fn aggregate_and_dump() {
let mut m = HybridLogScanMetrics::default();
m.add_scan_metric("Mutable", "Inline", 64);
m.add_scan_metric("Mutable", "Inline", 32);
m.add_scan_metric("Mutable", "OverflowBucket", 128);
m.add_scan_metric("ReadCache", "Inline", 16);
let dump = m.dump_scan_metrics_info();
assert!(dump.contains("# Region: Mutable\n"));
assert!(dump.contains(" State: Inline, Count: 2, Size: 96\n"));
assert!(dump.contains(" State: OverflowBucket, Count: 1, Size: 128\n"));
assert!(dump.contains("# Region: ReadCache\n"));
assert!(dump.find("Mutable").unwrap() < dump.find("ReadCache").unwrap());
}
#[test]
fn empty_dump_is_empty() {
assert_eq!(HybridLogScanMetrics::default().dump_scan_metrics_info(), "");
}
}