use std::sync::Mutex;
use std::time::{Duration, Instant};
use hallouminate_config::DaemonConfig;
pub(crate) struct MaintenanceDebt {
pub(crate) fragments: u64,
pub(crate) stale_versions: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DebtLevel {
Ok,
Soft,
Hard,
}
pub(super) fn classify(debt: &MaintenanceDebt, cfg: &DaemonConfig) -> DebtLevel {
if debt.fragments >= cfg.debt_hard_fragments
|| debt.stale_versions >= cfg.debt_hard_stale_versions
{
DebtLevel::Hard
} else if debt.fragments >= cfg.debt_soft_fragments
|| debt.stale_versions >= cfg.debt_soft_stale_versions
{
DebtLevel::Soft
} else {
DebtLevel::Ok
}
}
pub(super) struct DebtCache {
last: Mutex<Option<(DebtLevel, Instant)>>,
}
impl DebtCache {
pub(super) const fn new() -> Self {
Self {
last: Mutex::new(None),
}
}
pub(super) fn record(&self, level: DebtLevel) {
self.record_at(level, Instant::now());
}
pub(super) fn record_at(&self, level: DebtLevel, at: Instant) {
*self.last.lock().expect("debt cache lock") = Some((level, at));
}
pub(super) fn fresh_level(&self, ttl: Duration) -> Option<DebtLevel> {
self.fresh_level_at(ttl, Instant::now())
}
pub(super) fn fresh_level_at(&self, ttl: Duration, now: Instant) -> Option<DebtLevel> {
let last = *self.last.lock().expect("debt cache lock");
last.filter(|&(_, at)| now.saturating_duration_since(at) < ttl)
.map(|(level, _)| level)
}
pub(super) fn level(&self) -> DebtLevel {
let last = *self.last.lock().expect("debt cache lock");
last.map(|(level, _)| level).unwrap_or(DebtLevel::Ok)
}
}
pub(super) static OBSERVED: DebtCache = DebtCache::new();
#[cfg(test)]
pub(super) static OBSERVED_HARD_COORD: tokio::sync::RwLock<()> = tokio::sync::RwLock::const_new(());
pub(super) fn level() -> DebtLevel {
#[cfg(test)]
if let Some(level) = TEST_LEVEL.with(std::cell::Cell::get) {
return level;
}
OBSERVED.level()
}
#[cfg(test)]
thread_local! {
static TEST_LEVEL: std::cell::Cell<Option<DebtLevel>> = const { std::cell::Cell::new(None) };
}
#[cfg(test)]
pub(super) fn set_test_level(level: Option<DebtLevel>) {
TEST_LEVEL.with(|cell| cell.set(level));
}