common_memory_manager/
manager.rs1use std::sync::Arc;
16use std::sync::atomic::{AtomicU64, Ordering};
17
18use snafu::ensure;
19use tokio::sync::{OwnedSemaphorePermit, Semaphore, TryAcquireError};
20
21use crate::error::{
22 MemoryAcquireTimeoutSnafu, MemoryLimitExceededSnafu, MemorySemaphoreClosedSnafu, Result,
23};
24use crate::granularity::PermitGranularity;
25use crate::guard::MemoryGuard;
26use crate::policy::OnExhaustedPolicy;
27
28pub trait MemoryMetrics: Clone + Send + Sync + 'static {
30 fn set_limit(&self, bytes: i64);
31 fn set_in_use(&self, bytes: i64);
32 fn inc_rejected(&self, reason: &str);
33}
34
35#[derive(Clone)]
37pub struct MemoryManager<M: MemoryMetrics> {
38 quota: MemoryQuotaState<M>,
39}
40
41impl<M: MemoryMetrics + Default> Default for MemoryManager<M> {
42 fn default() -> Self {
43 Self::new(0, M::default())
44 }
45}
46
47#[derive(Clone)]
48pub(crate) struct MemoryQuota<M: MemoryMetrics> {
49 pub(crate) semaphore: Arc<Semaphore>,
50 pub(crate) limit_permits: u32,
51 pub(crate) granularity: PermitGranularity,
52 pub(crate) metrics: M,
53}
54
55#[derive(Clone)]
56pub(crate) struct UnlimitedMemoryQuota<M: MemoryMetrics> {
57 pub(crate) current_bytes: Arc<AtomicU64>,
58 pub(crate) metrics: M,
59}
60
61#[derive(Clone)]
62pub(crate) enum MemoryQuotaState<M: MemoryMetrics> {
63 Unlimited(UnlimitedMemoryQuota<M>),
64 Limited(MemoryQuota<M>),
65}
66
67impl<M: MemoryMetrics> MemoryManager<M> {
68 pub fn new(limit_bytes: u64, metrics: M) -> Self {
71 Self::with_granularity(limit_bytes, PermitGranularity::default(), metrics)
72 }
73
74 pub fn with_granularity(limit_bytes: u64, granularity: PermitGranularity, metrics: M) -> Self {
76 if limit_bytes == 0 {
77 metrics.set_limit(0);
78 return Self {
79 quota: MemoryQuotaState::Unlimited(UnlimitedMemoryQuota {
80 current_bytes: Arc::new(AtomicU64::new(0)),
81 metrics,
82 }),
83 };
84 }
85
86 let limit_permits = granularity.bytes_to_permits(limit_bytes);
87 let limit_aligned_bytes = granularity.permits_to_bytes(limit_permits);
88 metrics.set_limit(limit_aligned_bytes as i64);
89
90 Self {
91 quota: MemoryQuotaState::Limited(MemoryQuota {
92 semaphore: Arc::new(Semaphore::new(limit_permits as usize)),
93 limit_permits,
94 granularity,
95 metrics,
96 }),
97 }
98 }
99
100 pub fn limit_bytes(&self) -> u64 {
102 match &self.quota {
103 MemoryQuotaState::Unlimited(_) => 0,
104 MemoryQuotaState::Limited(quota) => quota.permits_to_bytes(quota.limit_permits),
105 }
106 }
107
108 pub fn used_bytes(&self) -> u64 {
110 match &self.quota {
111 MemoryQuotaState::Unlimited(quota) => quota.current_bytes.load(Ordering::Acquire),
112 MemoryQuotaState::Limited(quota) => quota.permits_to_bytes(quota.used_permits()),
113 }
114 }
115
116 pub fn available_bytes(&self) -> u64 {
120 match &self.quota {
121 MemoryQuotaState::Unlimited(_) => u64::MAX,
122 MemoryQuotaState::Limited(quota) => {
123 quota.permits_to_bytes(quota.available_permits_clamped())
124 }
125 }
126 }
127
128 pub async fn acquire(&self, bytes: u64) -> Result<MemoryGuard<M>> {
134 match &self.quota {
135 MemoryQuotaState::Unlimited(quota) => Ok(MemoryGuard::unlimited(quota.clone(), bytes)),
136 MemoryQuotaState::Limited(quota) => {
137 let permits = quota.bytes_to_permits(bytes);
138
139 ensure!(
140 permits <= quota.limit_permits,
141 MemoryLimitExceededSnafu {
142 requested_bytes: bytes,
143 limit_bytes: self.limit_bytes()
144 }
145 );
146
147 let permit = quota
148 .semaphore
149 .clone()
150 .acquire_many_owned(permits)
151 .await
152 .map_err(|_| MemorySemaphoreClosedSnafu.build())?;
153 quota.update_in_use_metric();
154 Ok(MemoryGuard::limited(quota.clone(), permit))
155 }
156 }
157 }
158
159 pub fn try_acquire(&self, bytes: u64) -> Option<MemoryGuard<M>> {
161 match &self.quota {
162 MemoryQuotaState::Unlimited(quota) => {
163 Some(MemoryGuard::unlimited(quota.clone(), bytes))
164 }
165 MemoryQuotaState::Limited(quota) => {
166 let permits = quota.bytes_to_permits(bytes);
167
168 match quota.semaphore.clone().try_acquire_many_owned(permits) {
169 Ok(permit) => {
170 quota.update_in_use_metric();
171 Some(MemoryGuard::limited(quota.clone(), permit))
172 }
173 Err(TryAcquireError::NoPermits) | Err(TryAcquireError::Closed) => {
174 quota.metrics.inc_rejected("try_acquire");
175 None
176 }
177 }
178 }
179 }
180 }
181
182 pub async fn acquire_with_policy(
192 &self,
193 bytes: u64,
194 policy: OnExhaustedPolicy,
195 ) -> Result<MemoryGuard<M>> {
196 match policy {
197 OnExhaustedPolicy::Wait { timeout } => {
198 match tokio::time::timeout(timeout, self.acquire(bytes)).await {
199 Ok(Ok(guard)) => Ok(guard),
200 Ok(Err(e)) => Err(e),
201 Err(_elapsed) => {
202 MemoryAcquireTimeoutSnafu {
204 requested_bytes: bytes,
205 waited: timeout,
206 }
207 .fail()
208 }
209 }
210 }
211 OnExhaustedPolicy::Fail => self.try_acquire(bytes).ok_or_else(|| {
212 MemoryLimitExceededSnafu {
213 requested_bytes: bytes,
214 limit_bytes: self.limit_bytes(),
215 }
216 .build()
217 }),
218 }
219 }
220}
221
222impl<M: MemoryMetrics> MemoryQuota<M> {
223 pub(crate) fn bytes_to_permits(&self, bytes: u64) -> u32 {
224 self.granularity.bytes_to_permits(bytes)
225 }
226
227 pub(crate) fn permits_to_bytes(&self, permits: u32) -> u64 {
228 self.granularity.permits_to_bytes(permits)
229 }
230
231 pub(crate) fn used_permits(&self) -> u32 {
232 self.limit_permits
233 .saturating_sub(self.available_permits_clamped())
234 }
235
236 pub(crate) fn available_permits_clamped(&self) -> u32 {
237 self.semaphore
238 .available_permits()
239 .min(self.limit_permits as usize) as u32
240 }
241
242 pub(crate) fn update_in_use_metric(&self) {
243 let bytes = self.permits_to_bytes(self.used_permits());
244 self.metrics.set_in_use(bytes as i64);
245 }
246
247 pub(crate) fn release_permit(&self, permit: OwnedSemaphorePermit) {
248 drop(permit);
249 self.update_in_use_metric();
250 }
251}
252
253impl<M: MemoryMetrics> UnlimitedMemoryQuota<M> {
254 pub(crate) fn add_in_use(&self, bytes: u64) {
255 if bytes == 0 {
256 return;
257 }
258
259 let previous = self
260 .current_bytes
261 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
262 Some(current.saturating_add(bytes))
263 })
264 .unwrap();
265 let new_total = previous.saturating_add(bytes);
266 debug_assert!(
267 new_total >= previous,
268 "unlimited memory usage counter overflowed"
269 );
270 self.metrics.set_in_use(new_total as i64);
271 }
272
273 pub(crate) fn sub_in_use(&self, bytes: u64) {
274 if bytes == 0 {
275 return;
276 }
277
278 let previous = self
279 .current_bytes
280 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
281 Some(current.saturating_sub(bytes))
282 })
283 .unwrap();
284 debug_assert!(
285 previous >= bytes,
286 "unlimited memory usage counter underflowed: current={previous}, release={bytes}"
287 );
288 let new_total = previous.saturating_sub(bytes);
289 self.metrics.set_in_use(new_total as i64);
290 }
291}