use std::sync::atomic::{AtomicU8, Ordering};
use rocksdb::{Env, Options, SstFileManager};
use super::TARGET;
use crate::kvs::Result;
use crate::kvs::rocksdb::RocksDbConfig;
const MAX_PERCENTAGE_USAGE: u8 = 80;
#[derive(Default, Eq, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub(super) enum DiskSpaceState {
#[default]
Normal,
ReadAndDeletionOnly,
}
#[derive(Default, Eq, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub(super) enum TransactionState {
#[default]
ReadsOnly,
HasDeletes,
HasWrites,
}
pub(super) struct DiskSpaceManager {
sst_file_manager: SstFileManager,
latest_state: AtomicU8,
max_allowed_space_usage_limit: u64,
}
impl DiskSpaceManager {
pub(super) fn configure(opts: &mut Options, config: &RocksDbConfig) -> Result<bool> {
let limit = config.sst_max_allowed_space_usage;
if limit > 0 {
info!(target: TARGET, "Disk space manager: enabled (limit={limit}B)");
opts.set_max_manifest_file_size(64 * 1024 * 1024);
opts.set_max_log_file_size(16 * 1024 * 1024);
opts.set_keep_log_file_num(3);
Ok(true)
} else {
info!(target: TARGET, "Disk space manager: disabled");
Ok(false)
}
}
pub(super) fn new(opts: &mut Options, config: &RocksDbConfig) -> Result<Self> {
let limit = config.sst_max_allowed_space_usage;
let env = Env::new()?;
let sst_file_manager = SstFileManager::new(&env)?;
sst_file_manager.set_max_allowed_space_usage(0);
opts.set_sst_file_manager(&sst_file_manager);
Ok(Self {
sst_file_manager,
latest_state: AtomicU8::new(DiskSpaceState::Normal as u8),
max_allowed_space_usage_limit: limit,
})
}
pub(super) fn usage(&self) -> u8 {
let current_size = self.sst_file_manager.get_total_size();
let allowed_size = self.max_allowed_space_usage_limit;
((current_size as f64 / allowed_size as f64) * 100.0).round() as u8
}
pub(super) fn cached_state(&self) -> DiskSpaceState {
match self.latest_state.load(Ordering::Acquire) {
0 => DiskSpaceState::Normal,
1 => DiskSpaceState::ReadAndDeletionOnly,
_ => unreachable!(),
}
}
pub(super) fn latest_state(&self) -> DiskSpaceState {
let limit = self.max_allowed_space_usage_limit;
match self.usage() < MAX_PERCENTAGE_USAGE {
true => {
let state = DiskSpaceState::Normal;
let latest_state = self.cached_state();
if latest_state != state {
self.latest_state.store(state as u8, Ordering::Release);
warn!(target: TARGET, "SST file space is below the {MAX_PERCENTAGE_USAGE}% usage threshold of the {limit} byte limit");
warn!(target: TARGET, "Transitioning to normal disk mode due to disk space limit being within the threshold");
}
DiskSpaceState::Normal
}
false => {
let state = DiskSpaceState::ReadAndDeletionOnly;
let latest_state = self.cached_state();
if latest_state != state {
self.latest_state.store(state as u8, Ordering::Release);
warn!(target: TARGET, "SST file space is above the {MAX_PERCENTAGE_USAGE}% usage threshold of the {limit} byte limit");
warn!(target: TARGET, "Transitioning to read-and-deletion-only mode due to disk space limit being reached");
}
DiskSpaceState::ReadAndDeletionOnly
}
}
}
}