use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TieredRun {
pub id: u64,
pub size_bytes: u64,
pub entries: Vec<(String, Option<Vec<u8>>)>,
}
impl TieredRun {
pub fn new(id: u64, entries: Vec<(String, Option<Vec<u8>>)>) -> Self {
let size_bytes = entries
.iter()
.map(|(k, v)| (k.len() + v.as_ref().map(|v| v.len()).unwrap_or(1)) as u64)
.sum();
Self {
id,
size_bytes,
entries,
}
}
}
#[derive(Debug, Default, Clone)]
pub struct TieredManifest {
pub levels: Vec<Vec<TieredRun>>,
}
impl TieredManifest {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, level: usize, run: TieredRun) {
while self.levels.len() <= level {
self.levels.push(Vec::new());
}
self.levels[level].push(run);
}
pub fn level_run_count(&self, level: usize) -> usize {
self.levels.get(level).map(|v| v.len()).unwrap_or(0)
}
pub fn total_run_count(&self) -> usize {
self.levels.iter().map(|l| l.len()).sum()
}
}
pub struct TieredCompactionPlanner {
runs_per_level: usize,
}
impl TieredCompactionPlanner {
pub fn new(runs_per_level: usize) -> Self {
Self {
runs_per_level: runs_per_level.max(2),
}
}
pub fn runs_per_level(&self) -> usize {
self.runs_per_level
}
pub fn pick_level(&self, manifest: &TieredManifest) -> Option<usize> {
manifest
.levels
.iter()
.enumerate()
.find(|(_, runs)| runs.len() >= self.runs_per_level)
.map(|(i, _)| i)
}
pub fn merge(&self, manifest: &mut TieredManifest, level: usize, new_id: u64) {
let runs = std::mem::take(&mut manifest.levels[level]);
let merged_entries = merge_runs(&runs);
let merged = TieredRun::new(new_id, merged_entries);
manifest.push(level + 1, merged);
}
}
fn merge_runs(runs: &[TieredRun]) -> Vec<(String, Option<Vec<u8>>)> {
let mut out: BTreeMap<String, Option<Vec<u8>>> = BTreeMap::new();
for run in runs {
for (k, v) in &run.entries {
out.insert(k.clone(), v.clone());
}
}
out.into_iter().collect()
}
#[cfg(test)]
#[path = "tiered_compaction_tests.rs"]
mod tests;