use std::sync::Arc;
use tessarikv_constants::LEVELS;
use tessarikv_format::TableMeta;
use super::pick::{Busy, Pick, bytes_of};
#[derive(Debug, Clone, Copy)]
pub(crate) struct Settings {
pub(crate) level0_trigger: u32,
pub(crate) size_ratio_percent: u32,
pub(crate) min_merge: u32,
pub(crate) max_size_amplification_percent: u32,
}
pub(crate) fn pick(
levels: &[Vec<Arc<TableMeta>>; LEVELS],
settings: Settings,
busy: &Busy,
) -> Option<Pick> {
let runs = levels.first()?;
let trigger = usize::try_from(settings.level0_trigger).unwrap_or(usize::MAX);
if runs.is_empty() || runs.len() < trigger {
return None;
}
let bottom_level = LEVELS.checked_sub(1)?;
let bottom = levels.get(bottom_level)?;
let above = u128::from(bytes_of(runs));
let below = u128::from(bytes_of(bottom));
let pick = if above.saturating_mul(100)
>= u128::from(settings.max_size_amplification_percent).saturating_mul(below)
{
Pick {
output_level: bottom_level,
inputs: runs.clone(),
overlaps: bottom.clone(),
drop: false,
}
} else {
let take = by_size(runs, settings.size_ratio_percent);
let least = usize::try_from(settings.min_merge).unwrap_or(usize::MAX);
let window = if take >= least {
0..take
} else {
let width = runs
.len()
.saturating_sub(trigger)
.saturating_add(2)
.max(least)
.min(runs.len());
cheapest(runs, width)?
};
if window.len() < 2 {
return None;
}
Pick {
output_level: 0,
inputs: runs.get(window)?.to_vec(),
overlaps: Vec::new(),
drop: false,
}
};
Some(pick).filter(|pick| busy.allows(pick))
}
fn cheapest(runs: &[Arc<TableMeta>], width: usize) -> Option<std::ops::Range<usize>> {
let windows = runs.windows(width.max(1));
let (start, _) = windows
.map(bytes_of)
.enumerate()
.min_by_key(|(start, bytes)| (*bytes, *start))?;
Some(start..start.saturating_add(width))
}
fn by_size(runs: &[Arc<TableMeta>], ratio: u32) -> usize {
let mut merged: u128 = 0;
let mut taken: usize = 0;
for run in runs {
let limit = merged.saturating_mul(u128::from(ratio).saturating_add(100));
if taken > 0 && u128::from(run.size).saturating_mul(100) > limit {
break;
}
merged = merged.saturating_add(u128::from(run.size));
taken = taken.saturating_add(1);
}
taken
}