use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use crate::config::QueryDbConfig;
pub struct CompactionHandle {
stop: Arc<AtomicBool>,
thread: Option<thread::JoinHandle<CompactionResult>>,
}
#[derive(Debug, Clone, Default)]
pub struct CompactionResult {
pub nodes_reclaimed: usize,
pub edges_merged: usize,
pub fragmentation_before: f64,
pub fragmentation_after: f64,
pub triggered: bool,
}
#[must_use]
pub fn should_compact(
free_slots: usize,
total_slots: usize,
delta_count: usize,
csr_count: usize,
config: &QueryDbConfig,
) -> bool {
if total_slots == 0 {
return false;
}
let fragmentation = free_slots as f64 / total_slots as f64;
if fragmentation > config.compaction_fragmentation_threshold {
return true;
}
if csr_count > 0 {
let delta_ratio = delta_count as f64 / csr_count as f64;
if delta_ratio > config.compaction_delta_ratio_threshold {
return true;
}
}
false
}
impl CompactionHandle {
#[must_use]
pub fn new() -> Self {
Self {
stop: Arc::new(AtomicBool::new(false)),
thread: None,
}
}
pub fn stop(&self) {
self.stop.store(true, Ordering::Release);
}
#[must_use]
pub fn is_running(&self) -> bool {
self.thread.as_ref().is_some_and(|t| !t.is_finished())
}
pub fn join(mut self) -> Option<CompactionResult> {
self.thread.take().and_then(|t| t.join().ok())
}
}
impl Default for CompactionHandle {
fn default() -> Self {
Self::new()
}
}
impl Drop for CompactionHandle {
fn drop(&mut self) {
self.stop.store(true, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_compact_fragmentation() {
let config = QueryDbConfig::default();
assert!(should_compact(21, 100, 0, 1000, &config));
assert!(!should_compact(19, 100, 0, 1000, &config));
}
#[test]
fn should_compact_delta_ratio() {
let config = QueryDbConfig::default();
assert!(should_compact(0, 100, 110, 1000, &config));
assert!(!should_compact(0, 100, 90, 1000, &config));
}
#[test]
fn should_compact_empty_graph() {
let config = QueryDbConfig::default();
assert!(!should_compact(0, 0, 0, 0, &config));
}
#[test]
fn compaction_handle_lifecycle() {
let handle = CompactionHandle::new();
assert!(!handle.is_running());
handle.stop();
let result = handle.join();
assert!(result.is_none());
}
}