use crate::config::SorterConfig;
use crate::plan::kind::SortPlan;
use crate::snapshot::SorterSnapshot;
use crate::spec::SortSpec;
pub struct SortPlanner;
impl SortPlanner {
#[must_use]
pub fn plan(spec: &SortSpec, snap: &SorterSnapshot, cfg: &SorterConfig) -> SortPlan {
let available = snap.available_memory_bytes();
if spec.estimated_bytes() <= cfg.in_memory_ceiling_bytes()
&& available >= spec.estimated_bytes()
{
return SortPlan::InMemory;
}
let max_fan_in = Self::ration_fan_in(snap, cfg);
let run_buffer_bytes = Self::size_run_buffer(spec, snap, cfg, max_fan_in);
SortPlan::External {
run_buffer_bytes,
max_fan_in,
}
}
fn ration_fan_in(snap: &SorterSnapshot, cfg: &SorterConfig) -> u32 {
let share = snap.fd_headroom() / snap.active_external_sorts().saturating_add(1);
share.clamp(2, cfg.max_fan_in())
}
fn size_run_buffer(
spec: &SortSpec,
snap: &SorterSnapshot,
cfg: &SorterConfig,
max_fan_in: u32,
) -> usize {
let fan = u64::from(max_fan_in);
let two_pass_target = spec
.estimated_bytes()
.div_ceil(fan.saturating_mul(fan).max(1));
let mem_cap = snap
.available_memory_bytes()
.max(cfg.min_run_buffer_bytes());
let buffer = two_pass_target
.clamp(cfg.min_run_buffer_bytes(), cfg.max_run_buffer_bytes())
.min(mem_cap);
usize::try_from(buffer).unwrap_or(usize::MAX)
}
}