diff --git a/libs/utils/src/serde_percent.rs b/libs/utils/src/serde_percent.rs index 63b62b5f1e..36e874a161 100644 --- a/libs/utils/src/serde_percent.rs +++ b/libs/utils/src/serde_percent.rs @@ -11,6 +11,14 @@ use serde::{Deserialize, Serialize}; pub struct Percent(#[serde(deserialize_with = "deserialize_pct_0_to_100")] u8); impl Percent { + pub const fn new(pct: u8) -> Option { + if pct <= 100 { + Some(Percent(pct)) + } else { + None + } + } + pub fn get(&self) -> u8 { self.0 } diff --git a/pageserver/src/disk_usage_eviction_task.rs b/pageserver/src/disk_usage_eviction_task.rs index eeeb6fda89..f4a0f3f18e 100644 --- a/pageserver/src/disk_usage_eviction_task.rs +++ b/pageserver/src/disk_usage_eviction_task.rs @@ -639,7 +639,7 @@ mod filesystem_level_usage { ), ( "max_usage_pct", - usage_pct > self.config.max_usage_pct.get() as u64, + usage_pct >= self.config.max_usage_pct.get() as u64, ), ]; @@ -686,4 +686,43 @@ mod filesystem_level_usage { avail_bytes, }) } + + #[test] + fn max_usage_pct_pressure() { + use super::Usage as _; + use std::time::Duration; + use utils::serde_percent::Percent; + + let mut usage = Usage { + config: &DiskUsageEvictionTaskConfig { + max_usage_pct: Percent::new(85).unwrap(), + min_avail_bytes: 0, + period: Duration::MAX, + #[cfg(feature = "testing")] + mock_statvfs: None, + }, + total_bytes: 100_000, + avail_bytes: 0, + }; + + assert!(usage.has_pressure(), "expected pressure at 100%"); + + usage.add_available_bytes(14_000); + assert!(usage.has_pressure(), "expected pressure at 86%"); + + usage.add_available_bytes(999); + assert!(usage.has_pressure(), "expected pressure at 85.001%"); + + usage.add_available_bytes(1); + assert!(usage.has_pressure(), "expected pressure at precisely 85%"); + + usage.add_available_bytes(1); + assert!(!usage.has_pressure(), "no pressure at 84.999%"); + + usage.add_available_bytes(999); + assert!(!usage.has_pressure(), "no pressure at 84%"); + + usage.add_available_bytes(16_000); + assert!(!usage.has_pressure()); + } }