common_memory_manager/
manager.rs1use std::sync::Arc;
16
17use snafu::ensure;
18use tokio::sync::{Semaphore, TryAcquireError};
19
20use crate::error::{
21 MemoryAcquireTimeoutSnafu, MemoryLimitExceededSnafu, MemorySemaphoreClosedSnafu, Result,
22};
23use crate::granularity::PermitGranularity;
24use crate::guard::MemoryGuard;
25use crate::policy::OnExhaustedPolicy;
26
27pub trait MemoryMetrics: Clone + Send + Sync + 'static {
29 fn set_limit(&self, bytes: i64);
30 fn set_in_use(&self, bytes: i64);
31 fn inc_rejected(&self, reason: &str);
32}
33
34#[derive(Clone)]
36pub struct MemoryManager<M: MemoryMetrics> {
37 quota: Option<MemoryQuota<M>>,
38}
39
40#[derive(Clone)]
41pub(crate) struct MemoryQuota<M: MemoryMetrics> {
42 pub(crate) semaphore: Arc<Semaphore>,
43 pub(crate) limit_permits: u32,
44 pub(crate) granularity: PermitGranularity,
45 pub(crate) metrics: M,
46}
47
48impl<M: MemoryMetrics> MemoryManager<M> {
49 pub fn new(limit_bytes: u64, metrics: M) -> Self {
52 Self::with_granularity(limit_bytes, PermitGranularity::default(), metrics)
53 }
54
55 pub fn with_granularity(limit_bytes: u64, granularity: PermitGranularity, metrics: M) -> Self {
57 if limit_bytes == 0 {
58 metrics.set_limit(0);
59 return Self { quota: None };
60 }
61
62 let limit_permits = granularity.bytes_to_permits(limit_bytes);
63 let limit_aligned_bytes = granularity.permits_to_bytes(limit_permits);
64 metrics.set_limit(limit_aligned_bytes as i64);
65
66 Self {
67 quota: Some(MemoryQuota {
68 semaphore: Arc::new(Semaphore::new(limit_permits as usize)),
69 limit_permits,
70 granularity,
71 metrics,
72 }),
73 }
74 }
75
76 pub fn limit_bytes(&self) -> u64 {
78 self.quota
79 .as_ref()
80 .map(|quota| quota.permits_to_bytes(quota.limit_permits))
81 .unwrap_or(0)
82 }
83
84 pub fn used_bytes(&self) -> u64 {
86 self.quota
87 .as_ref()
88 .map(|quota| quota.permits_to_bytes(quota.used_permits()))
89 .unwrap_or(0)
90 }
91
92 pub fn available_bytes(&self) -> u64 {
94 self.quota
95 .as_ref()
96 .map(|quota| quota.permits_to_bytes(quota.available_permits_clamped()))
97 .unwrap_or(0)
98 }
99
100 pub async fn acquire(&self, bytes: u64) -> Result<MemoryGuard<M>> {
106 match &self.quota {
107 None => Ok(MemoryGuard::unlimited()),
108 Some(quota) => {
109 let permits = quota.bytes_to_permits(bytes);
110
111 ensure!(
112 permits <= quota.limit_permits,
113 MemoryLimitExceededSnafu {
114 requested_bytes: bytes,
115 limit_bytes: self.limit_bytes()
116 }
117 );
118
119 let permit = quota
120 .semaphore
121 .clone()
122 .acquire_many_owned(permits)
123 .await
124 .map_err(|_| MemorySemaphoreClosedSnafu.build())?;
125 quota.update_in_use_metric();
126 Ok(MemoryGuard::limited(permit, quota.clone()))
127 }
128 }
129 }
130
131 pub fn try_acquire(&self, bytes: u64) -> Option<MemoryGuard<M>> {
133 match &self.quota {
134 None => Some(MemoryGuard::unlimited()),
135 Some(quota) => {
136 let permits = quota.bytes_to_permits(bytes);
137
138 match quota.semaphore.clone().try_acquire_many_owned(permits) {
139 Ok(permit) => {
140 quota.update_in_use_metric();
141 Some(MemoryGuard::limited(permit, quota.clone()))
142 }
143 Err(TryAcquireError::NoPermits) | Err(TryAcquireError::Closed) => {
144 quota.metrics.inc_rejected("try_acquire");
145 None
146 }
147 }
148 }
149 }
150 }
151
152 pub async fn acquire_with_policy(
162 &self,
163 bytes: u64,
164 policy: OnExhaustedPolicy,
165 ) -> Result<MemoryGuard<M>> {
166 match policy {
167 OnExhaustedPolicy::Wait { timeout } => {
168 match tokio::time::timeout(timeout, self.acquire(bytes)).await {
169 Ok(Ok(guard)) => Ok(guard),
170 Ok(Err(e)) => Err(e),
171 Err(_elapsed) => {
172 MemoryAcquireTimeoutSnafu {
174 requested_bytes: bytes,
175 waited: timeout,
176 }
177 .fail()
178 }
179 }
180 }
181 OnExhaustedPolicy::Fail => self.try_acquire(bytes).ok_or_else(|| {
182 MemoryLimitExceededSnafu {
183 requested_bytes: bytes,
184 limit_bytes: self.limit_bytes(),
185 }
186 .build()
187 }),
188 }
189 }
190}
191
192impl<M: MemoryMetrics> MemoryQuota<M> {
193 pub(crate) fn bytes_to_permits(&self, bytes: u64) -> u32 {
194 self.granularity.bytes_to_permits(bytes)
195 }
196
197 pub(crate) fn permits_to_bytes(&self, permits: u32) -> u64 {
198 self.granularity.permits_to_bytes(permits)
199 }
200
201 pub(crate) fn used_permits(&self) -> u32 {
202 self.limit_permits
203 .saturating_sub(self.available_permits_clamped())
204 }
205
206 pub(crate) fn available_permits_clamped(&self) -> u32 {
207 self.semaphore
208 .available_permits()
209 .min(self.limit_permits as usize) as u32
210 }
211
212 pub(crate) fn update_in_use_metric(&self) {
213 let bytes = self.permits_to_bytes(self.used_permits());
214 self.metrics.set_in_use(bytes as i64);
215 }
216}