use super::error::QuotaError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Resource {
ResidentMemory,
DiskUsage,
}
impl Resource {
fn parameter(self) -> &'static str {
match self {
Resource::ResidentMemory => "max_resident_memory_percent",
Resource::DiskUsage => "max_disk_usage_percent",
}
}
fn total(self) -> &'static str {
match self {
Resource::ResidentMemory => "total memory",
Resource::DiskUsage => "total capacity",
}
}
fn description(self) -> &'static str {
match self {
Resource::ResidentMemory => "Resident memory usage",
Resource::DiskUsage => "Disk usage",
}
}
pub(super) fn rejected(self, used_percent: u8, limit: u8, threshold: u8) -> QuotaError {
let (description, total) = (self.description(), self.total());
let condition = if used_percent >= limit {
format!(
"{description} is at {used_percent}% of {total}, \
exceeding the configured limit of {limit}%",
)
} else {
format!(
"{description} is at {used_percent}% of {total}. It reached the configured limit \
of {limit}% and has to fall below {threshold}% before this node takes writes again",
)
};
let remedy = match self {
Resource::ResidentMemory => {
"Reduce memory usage (e.g. delete points or drop collections)"
}
Resource::DiskUsage => "Reduce disk usage (e.g. delete points or drop collections)",
};
QuotaError::LimitReached(format!(
"{condition}. Help: {remedy}, or raise `{}` in the global quota config.",
self.parameter(),
))
}
}
pub fn percent_of(used: u64, total: u64) -> Option<u8> {
if total == 0 {
return None;
}
let percent = u128::from(used) * 100 / u128::from(total);
Some(percent.min(100) as u8)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn utilization_is_a_clamped_percentage() {
assert_eq!(percent_of(0, 100), Some(0));
assert_eq!(percent_of(999, 1_000), Some(99));
assert_eq!(percent_of(200, 100), Some(100));
assert_eq!(percent_of(0, 0), None);
assert_eq!(percent_of(u64::MAX, u64::MAX), Some(100));
}
}