use crate::{
SequenceNumberCounter, TableId,
compaction::state::CompactionState,
config::Config,
deletion_pause::DeletionPause,
runtime_config::handle::RuntimeConfigHandle,
stop_signal::StopSignal,
version::{SuperVersions, Version, persist_version},
};
use alloc::sync::Arc;
use portable_atomic::AtomicU64;
#[cfg(feature = "std")]
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
#[cfg(not(feature = "std"))]
use spin::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
pub type VersionsWriteGuard<'a> = RwLockWriteGuard<'a, SuperVersions>;
pub type VersionsReadGuard<'a> = RwLockReadGuard<'a, SuperVersions>;
#[cfg(feature = "std")]
pub type CompactionGuard<'a> = parking_lot::MutexGuard<'a, CompactionState>;
#[cfg(not(feature = "std"))]
pub type CompactionGuard<'a> = spin::MutexGuard<'a, CompactionState, spin::Spin>;
#[cfg(feature = "std")]
pub type FlushGuard<'a> = parking_lot::MutexGuard<'a, ()>;
#[cfg(not(feature = "std"))]
pub type FlushGuard<'a> = spin::MutexGuard<'a, (), spin::Spin>;
#[cfg(feature = "metrics")]
use crate::metrics::Metrics;
pub type TreeId = u64;
pub type MemtableId = u64;
pub fn get_next_tree_id() -> TreeId {
static TREE_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
TREE_ID_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
}
#[cfg(test)]
pub type TestHook = Box<dyn Fn() + Send>;
#[cfg(test)]
#[derive(Default)]
pub struct TestHooks {
pub(crate) scan_freeze: std::sync::Mutex<Option<TestHook>>,
pub(crate) range_write: std::sync::Mutex<Option<TestHook>>,
pub(crate) upgrade: std::sync::Mutex<Option<TestHook>>,
}
#[cfg(test)]
impl TestHooks {
pub(crate) fn fire(slot: &std::sync::Mutex<Option<TestHook>>) {
#[expect(clippy::unwrap_used, reason = "test-only seam")]
let guard = slot.lock().unwrap();
if let Some(hook) = guard.as_ref() {
hook();
}
}
pub(crate) fn install(slot: &std::sync::Mutex<Option<TestHook>>, hook: TestHook) {
#[expect(clippy::unwrap_used, reason = "test-only seam")]
slot.lock().unwrap().replace(hook);
}
}
pub struct TreeInner {
pub id: TreeId,
pub(crate) life: Arc<crate::close::Life>,
pub(crate) closing: core::sync::atomic::AtomicBool,
#[doc(hidden)]
pub memtable_id_counter: SequenceNumberCounter,
#[doc(hidden)]
pub table_id_counter: SequenceNumberCounter,
pub(crate) table_files: Arc<crate::table::file_pool::TableFilePools>,
pub(crate) blob_file_id_counter: SequenceNumberCounter,
pub(crate) version_history: Arc<RwLock<SuperVersions>>,
#[cfg(feature = "std")]
pub(crate) latest_super_version: Arc<arc_swap::ArcSwap<crate::version::SuperVersion>>,
pub(crate) compaction_state: Arc<Mutex<CompactionState>>,
pub config: Arc<Config>,
pub(crate) stop_signal: StopSignal,
pub(crate) major_compaction_lock: RwLock<()>,
pub(crate) flush_lock: Mutex<()>,
#[cfg(feature = "std")]
pub(crate) directory_lock: Option<Arc<dyn crate::fs::FsFile>>,
pub(crate) deletion_pause: Arc<DeletionPause>,
#[cfg(feature = "std")]
pub(crate) background_deleter: Arc<crate::BackgroundDeleter>,
pub(crate) heal_hints: Arc<crate::heal_hints::HealHints>,
pub(crate) filter_budget: Arc<crate::filter_budget::FilterBudget>,
pub(crate) compaction_rate_limiter: Arc<crate::rate_limiter::RateLimiter>,
pub(crate) compaction_capacity: Arc<crate::CapacityPool>,
pub(crate) write_rate_limiter: Arc<crate::rate_limiter::RateLimiter>,
pub(crate) runtime_config: Arc<RuntimeConfigHandle>,
pub(crate) kv_digest_at_insert: portable_atomic::AtomicU8,
pub(crate) admission_used_cache: Mutex<Option<(u64, u64, u64, crate::time::Instant)>>,
#[cfg(zstd_any)]
pub(crate) retired_write_snapshots:
Mutex<alloc::vec::Vec<alloc::sync::Weak<crate::runtime_config::RuntimeConfig>>>,
#[doc(hidden)]
#[cfg(feature = "metrics")]
pub metrics: Arc<Metrics>,
#[cfg(test)]
pub(crate) test_hooks: TestHooks,
}
impl TreeInner {
pub(crate) fn create_new(
mut config: Config,
#[cfg(feature = "std")] directory_lock: Option<Box<dyn crate::fs::FsFile>>,
) -> crate::Result<Self> {
let version = Version::new(
0,
if config.kv_separation_opts.is_some() {
crate::TreeType::Blob
} else {
crate::TreeType::Standard
},
);
let initial_runtime = Arc::new(config.initial_runtime_config.clone());
persist_version(
&config.path,
&version,
config.comparator.name(),
&*config.fs,
Arc::clone(&initial_runtime),
config.encryption.clone(),
config.sync_mode,
)?;
let comparator = config.comparator.clone();
let sync_mode = config.sync_mode;
let life = crate::close::Life::new();
let table_id_counter = SequenceNumberCounter::default();
let table_files = Arc::new(crate::table::file_pool::TableFilePools::for_tree(
table_id_counter.clone(),
config.table_file_pool,
sync_mode,
config.table_file_refill,
Arc::clone(&life),
));
let compaction_rate_limiter = config.tree_compaction_rate_limiter();
config.live_compaction_rate_limiter = Some(Arc::clone(&compaction_rate_limiter));
let compaction_capacity = config.tree_compaction_capacity();
let write_rate_limiter = config.tree_write_rate_limiter();
let stop_signal = StopSignal::default();
#[cfg(feature = "std")]
stop_signal.wake_on_stop(&compaction_rate_limiter);
let snapshot_id = version.id();
let filter_budget = Arc::<crate::filter_budget::FilterBudget>::default();
let mut super_versions = SuperVersions::new(
version,
&comparator,
sync_mode,
snapshot_id,
config.manifest_log_rotate_bytes,
);
if config.filter_advisor.is_some() {
super_versions = super_versions.with_filter_budget(Arc::clone(&filter_budget));
}
#[cfg(feature = "std")]
let latest_super_version = super_versions.latest_handle();
#[cfg(feature = "std")]
if let Some(budget) = &config.write_buffer_budget {
budget.register(Arc::downgrade(&latest_super_version));
}
Ok(Self {
id: get_next_tree_id(),
deletion_pause: DeletionPause::for_tree(Arc::clone(&life)),
#[cfg(feature = "std")]
background_deleter: Arc::new(crate::BackgroundDeleter::for_tree(
None,
Arc::clone(&life),
)),
life,
closing: core::sync::atomic::AtomicBool::new(false),
memtable_id_counter: SequenceNumberCounter::new(1),
table_id_counter,
table_files,
blob_file_id_counter: SequenceNumberCounter::default(),
config: Arc::new(config),
version_history: Arc::new(RwLock::new(super_versions)),
#[cfg(feature = "std")]
latest_super_version,
stop_signal,
major_compaction_lock: RwLock::default(),
flush_lock: Mutex::default(),
#[cfg(feature = "std")]
directory_lock: directory_lock.map(Arc::from),
compaction_state: Arc::new(Mutex::new(CompactionState::default())),
heal_hints: crate::heal_hints::HealHints::new_shared(initial_runtime.auto_heal),
filter_budget,
compaction_rate_limiter,
compaction_capacity,
write_rate_limiter,
kv_digest_at_insert: portable_atomic::AtomicU8::new(kv_digest_at_insert_gate(
&initial_runtime,
)),
runtime_config: Arc::new(RuntimeConfigHandle::new((*initial_runtime).clone())),
admission_used_cache: Mutex::new(None),
#[cfg(zstd_any)]
retired_write_snapshots: Mutex::new(alloc::vec::Vec::new()),
#[cfg(feature = "metrics")]
metrics: Metrics::default().into(),
#[cfg(test)]
test_hooks: TestHooks::default(),
})
}
pub fn get_next_table_id(&self) -> TableId {
self.table_id_counter.next()
}
}
pub fn kv_digest_at_insert_gate(rc: &crate::runtime_config::RuntimeConfig) -> u8 {
use crate::runtime_config::{KvChecksumComputePoint, KvChecksumPolicy};
if matches!(
rc.kv_checksum_compute_point,
KvChecksumComputePoint::AtInsert
) && !matches!(rc.kv_checksums, KvChecksumPolicy::Off)
{
rc.kv_checksum_algo.wire_tag() + 1
} else {
0
}
}
#[inline]
pub fn kv_digest_algo_from_gate(gate: u8) -> Option<crate::runtime_config::ChecksumAlgorithm> {
gate.checked_sub(1)
.and_then(crate::runtime_config::ChecksumAlgorithm::from_wire_tag)
}
impl Drop for TreeInner {
fn drop(&mut self) {
log::debug!("Dropping TreeInner");
log::trace!("Sending stop signal to compactors");
self.stop_signal.send();
self.compaction_capacity.forget(self.id);
}
}