linkrs_query/executor/streaming/spill/
quota.rs1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::Arc;
3
4use linkrs_core::error::QueryError;
5
6#[derive(Debug, Clone)]
13pub struct DiskQuota {
14 max_bytes: u64,
15 used: Arc<AtomicU64>,
16}
17
18impl DiskQuota {
19 pub fn new(max_bytes: u64) -> Self {
21 Self {
22 max_bytes,
23 used: Arc::new(AtomicU64::new(0)),
24 }
25 }
26
27 pub fn default_quota() -> Self {
29 Self::new(2 * 1024 * 1024 * 1024)
30 }
31
32 pub fn try_reserve(&self, bytes: u64) -> Result<(), QueryError> {
35 if self.max_bytes == 0 {
36 let _ = self.used.fetch_add(bytes, Ordering::Relaxed);
38 return Ok(());
39 }
40 let mut prev = self.used.load(Ordering::Relaxed);
41 loop {
42 let total = prev.checked_add(bytes).ok_or_else(|| {
43 QueryError::execution(format!(
44 "Disk quota overflow: request {} bytes overflows u64",
45 bytes,
46 ))
47 })?;
48 if total > self.max_bytes {
49 return Err(QueryError::execution(format!(
50 "Disk quota exceeded: request {} bytes, total {} > quota {} bytes",
51 bytes, total, self.max_bytes,
52 )));
53 }
54 match self
55 .used
56 .compare_exchange_weak(prev, total, Ordering::Relaxed, Ordering::Relaxed)
57 {
58 Ok(_) => return Ok(()),
59 Err(current) => prev = current,
60 }
61 }
62 }
63
64 pub fn release(&self, bytes: u64) {
66 self.used.fetch_sub(bytes, Ordering::Relaxed);
67 }
68
69 pub fn current(&self) -> u64 {
71 self.used.load(Ordering::Relaxed)
72 }
73
74 pub fn max(&self) -> u64 {
76 self.max_bytes
77 }
78}