pub(crate) mod fifo;
pub(crate) mod leveled;
#[cfg(feature = "std")]
pub(crate) mod delete_materialize;
pub(crate) mod drop_range;
pub mod filter;
mod flavour;
pub(crate) mod heal;
pub(crate) mod major;
pub(crate) mod movedown;
pub(crate) mod pulldown;
pub(crate) mod seqno_zeroer;
pub(crate) mod state;
pub(crate) mod stream;
pub(crate) mod tiered;
pub(crate) mod worker;
pub use fifo::Strategy as Fifo;
pub use filter::{CompactionFilter, Factory, ItemAccessor, Verdict};
pub use heal::Strategy as EccHeal;
pub use leveled::Strategy as Leveled;
pub use tiered::Strategy as SizeTiered;
pub use {
fifo::NAME as FIFO_COMPACTION_NAME, leveled::NAME as LEVELED_COMPACTION_NAME,
tiered::NAME as TIERED_COMPACTION_NAME,
};
pub type Levelled = Leveled;
#[doc(hidden)]
pub use movedown::Strategy as MoveDown;
#[doc(hidden)]
pub use pulldown::Strategy as PullDown;
use crate::{
HashSet, KvPair, TableId, compaction::state::CompactionState, config::Config, version::Version,
};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompactionAction {
Nothing,
Merged,
Moved,
Dropped,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompactionResult {
pub action: CompactionAction,
pub dest_level: Option<u8>,
pub tables_in: usize,
pub tables_out: usize,
}
impl CompactionResult {
#[must_use]
pub fn nothing() -> Self {
Self {
action: CompactionAction::Nothing,
dest_level: None,
tables_in: 0,
tables_out: 0,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Input {
pub table_ids: HashSet<TableId>,
pub dest_level: u8,
pub canonical_level: u8,
pub target_size: u64,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Choice {
DoNothing,
Move(Input),
Merge(Input),
Drop(HashSet<TableId>),
}
#[expect(clippy::module_name_repetitions)]
pub trait CompactionStrategy: Send + Sync {
fn get_name(&self) -> &'static str;
#[doc(hidden)]
fn get_config(&self) -> Vec<KvPair> {
vec![]
}
fn choose(&self, version: &Version, config: &Config, state: &CompactionState) -> Choice;
fn pending_compaction_bytes(&self, _version: &Version) -> u64 {
0
}
}
pub(crate) fn choose_with_density_rewrite(
strategy: &dyn CompactionStrategy,
version: &Version,
config: &Config,
runtime_config: &crate::runtime_config::RuntimeConfig,
state: &CompactionState,
) -> Choice {
let structural = strategy.choose(version, config, state);
if matches!(structural, Choice::DoNothing)
&& let Some(input) = pick_density_rewrite(version, runtime_config, state)
{
return Choice::Merge(input);
}
structural
}
pub(crate) fn pick_density_rewrite(
version: &Version,
runtime_config: &crate::runtime_config::RuntimeConfig,
state: &CompactionState,
) -> Option<Input> {
let mut best: Option<(u8, TableId, usize, u64)> = None;
for (level_idx, level) in version.iter_levels().enumerate() {
let crate::config::DeleteStrategy::Adaptive {
purge_threshold_percent,
} = runtime_config.delete_strategy.get(level_idx)
else {
continue;
};
for table in level.iter().flat_map(|run| run.iter()) {
if state.hidden_set().is_hidden(table.id()) {
continue;
}
let Some(density) = table.delete_density() else {
continue;
};
if density < purge_threshold_percent {
continue;
}
let take = match best {
None => true,
Some((best_density, _, _, _)) => density > best_density,
};
if take {
best = Some((density, table.id(), level_idx, table.file_size()));
}
}
}
let (_, table_id, level_idx, file_size) = best?;
let mut table_ids = HashSet::default();
table_ids.insert(table_id);
#[expect(
clippy::cast_possible_truncation,
reason = "level index is bounded by level count (<= 7, technically 255)"
)]
let level = level_idx as u8;
Some(Input {
table_ids,
dest_level: level,
canonical_level: level,
target_size: file_size,
})
}