use re_log_encoding::RawRrdManifest;
use crate::error::Error;
use crate::view::ChunkIndexView;
#[derive(Clone, Debug)]
pub struct ChunkIndexAnalysis {
pub merge: MergeAssessment,
pub num_columns: usize,
}
pub fn analyze_chunk_index(
chunk_index: &RawRrdManifest,
chunk_max_bytes: u64,
) -> Result<ChunkIndexAnalysis, Error> {
let view = ChunkIndexView::try_from_raw(chunk_index)?;
Ok(ChunkIndexAnalysis {
merge: MergeAssessment::compute(&view, chunk_max_bytes),
num_columns: view.num_columns,
})
}
const UNOPTIMIZED_FACTOR_THRESHOLD: f64 = 2.0;
const UNOPTIMIZED_EXCESS_THRESHOLD: u64 = 200;
#[derive(Clone, Copy, Debug, Default)]
pub struct MergeAssessment {
pub actual_chunks: u64,
pub achievable_chunks: u64,
pub excess_chunks: u64,
pub factor: f64,
}
impl MergeAssessment {
pub fn compute(view: &ChunkIndexView, chunk_max_bytes: u64) -> Self {
let chunk_max_bytes = chunk_max_bytes.max(1);
let mut actual_chunks = 0u64;
let mut achievable_chunks = 0u64;
for entity in view.entities.values() {
for group in &entity.timeline_sets {
let group_chunks = group.num_chunks() as u64;
let group_bytes: u64 = group
.per_timeline
.values()
.next()
.into_iter()
.flatten()
.map(|span| view.chunk(span.chunk).byte_size_uncompressed)
.sum();
let group_achievable = group_bytes
.div_ceil(chunk_max_bytes)
.clamp(1, group_chunks.max(1));
actual_chunks += group_chunks;
achievable_chunks += group_achievable.min(group_chunks);
}
}
Self {
actual_chunks,
achievable_chunks,
excess_chunks: actual_chunks - achievable_chunks,
factor: if achievable_chunks == 0 {
1.0
} else {
actual_chunks as f64 / achievable_chunks as f64
},
}
}
pub fn looks_unoptimized(&self) -> bool {
self.looks_unoptimized_with(UNOPTIMIZED_FACTOR_THRESHOLD, UNOPTIMIZED_EXCESS_THRESHOLD)
}
pub fn looks_unoptimized_with(&self, factor_threshold: f64, excess_threshold: u64) -> bool {
self.factor >= factor_threshold && self.excess_chunks >= excess_threshold
}
}