use serde::{Deserialize, Serialize}; use crate::lsm_storage::LsmStorageState; #[derive(Debug, Clone)] pub struct SimpleLeveledCompactionOptions { pub size_ratio_percent: usize, pub level0_file_num_compaction_trigger: usize, pub max_levels: usize, } #[derive(Serialize, Deserialize)] pub struct SimpleLeveledCompactionTask { // if upper_level is `None`, then it is L0 compaction pub upper_level: Option, pub upper_level_sst_ids: Vec, pub lower_level: usize, pub lower_level_sst_ids: Vec, pub is_lower_level_bottom_level: bool, } pub struct SimpleLeveledCompactionController { options: SimpleLeveledCompactionOptions, } impl SimpleLeveledCompactionController { pub fn new(options: SimpleLeveledCompactionOptions) -> Self { Self { options } } pub fn generate_compaction_task( &self, snapshot: &LsmStorageState, ) -> Option { let mut level_sizes = Vec::new(); level_sizes.push(snapshot.l0_sstables.len()); for (_, files) in &snapshot.levels { level_sizes.push(files.len()); } for i in 0..self.options.max_levels { if i == 0 && snapshot.l0_sstables.len() < self.options.level0_file_num_compaction_trigger { continue; } let lower_level = i + 1; let size_ratio = level_sizes[lower_level] as f64 / level_sizes[i] as f64; if size_ratio < self.options.size_ratio_percent as f64 / 100.0 { println!( "compaction triggered at level {} and {} with size ratio {}", i, lower_level, size_ratio ); return Some(SimpleLeveledCompactionTask { upper_level: if i == 0 { None } else { Some(i) }, upper_level_sst_ids: if i == 0 { snapshot.l0_sstables.clone() } else { snapshot.levels[i - 1].1.clone() }, lower_level, lower_level_sst_ids: snapshot.levels[lower_level - 1].1.clone(), is_lower_level_bottom_level: lower_level == self.options.max_levels, }); } } None } pub fn apply_compaction_result( &self, snapshot: &LsmStorageState, task: &SimpleLeveledCompactionTask, output: &[usize], ) -> (LsmStorageState, Vec) { let mut snapshot = snapshot.clone(); let mut files_to_remove = Vec::new(); if let Some(upper_level) = task.upper_level { assert_eq!( task.upper_level_sst_ids, snapshot.levels[upper_level - 1].1, "sst mismatched" ); files_to_remove.extend(&snapshot.levels[upper_level - 1].1); snapshot.levels[upper_level - 1].1.clear(); } else { assert_eq!( task.upper_level_sst_ids, snapshot.l0_sstables, "sst mismatched" ); files_to_remove.extend(&snapshot.l0_sstables); snapshot.l0_sstables.clear(); } assert_eq!( task.lower_level_sst_ids, snapshot.levels[task.lower_level - 1].1, "sst mismatched" ); files_to_remove.extend(&snapshot.levels[task.lower_level - 1].1); snapshot.levels[task.lower_level - 1].1 = output.to_vec(); (snapshot, files_to_remove) } }