use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use linkrs_core::error::QueryError;
#[derive(Debug, Clone)]
pub struct DiskQuota {
max_bytes: u64,
used: Arc<AtomicU64>,
}
impl DiskQuota {
pub fn new(max_bytes: u64) -> Self {
Self {
max_bytes,
used: Arc::new(AtomicU64::new(0)),
}
}
pub fn default_quota() -> Self {
Self::new(2 * 1024 * 1024 * 1024)
}
pub fn try_reserve(&self, bytes: u64) -> Result<(), QueryError> {
if self.max_bytes == 0 {
let _ = self.used.fetch_add(bytes, Ordering::Relaxed);
return Ok(());
}
let mut prev = self.used.load(Ordering::Relaxed);
loop {
let total = prev.checked_add(bytes).ok_or_else(|| {
QueryError::execution(format!(
"Disk quota overflow: request {} bytes overflows u64",
bytes,
))
})?;
if total > self.max_bytes {
return Err(QueryError::execution(format!(
"Disk quota exceeded: request {} bytes, total {} > quota {} bytes",
bytes, total, self.max_bytes,
)));
}
match self
.used
.compare_exchange_weak(prev, total, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => return Ok(()),
Err(current) => prev = current,
}
}
}
pub fn release(&self, bytes: u64) {
self.used.fetch_sub(bytes, Ordering::Relaxed);
}
pub fn current(&self) -> u64 {
self.used.load(Ordering::Relaxed)
}
pub fn max(&self) -> u64 {
self.max_bytes
}
}