1use std::collections::BTreeMap;
20use std::sync::atomic::{AtomicUsize, Ordering};
21use std::sync::{Arc, Mutex};
22
23use tokio::time::Instant;
25
26use super::arrow::RetainedMemory;
28use super::manager::{Inner, State};
29use crate::{Error, Result};
30
31#[derive(Clone, Default)]
40pub(super) struct Held(Arc<AtomicUsize>);
41
42pub(super) struct Reservation {
43 inner: Arc<Inner>,
44 pool: Pool,
45 bytes: usize,
46 held: Held,
47 lifecycle: bool,
48}
49
50#[derive(Default)]
51pub(super) struct Growers {
52 next: u64,
53 waiting: BTreeMap<u64, Grower>,
54}
55
56struct Grower {
57 held: Held,
58 need: usize,
59}
60
61struct Waiting {
62 inner: Arc<Inner>,
63 pool: Pool,
64 id: u64,
65 active: bool,
66}
67
68#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub(super) enum Pool {
72 Read,
73
74 Write,
75}
76
77#[derive(Debug)]
78pub(super) enum Refusal {
79 Exceeded(Pool),
80
81 Busy,
82
83 Cycle,
84
85 Elapsed,
86
87 Failed(Error),
88}
89
90pub(super) enum Attempt<T> {
91 Ready(T),
92 Wait,
93 Refuse(Refusal),
94}
95
96impl Held {
101 pub(super) fn bytes(&self) -> usize {
102 self.0.load(Ordering::SeqCst)
103 }
104}
105
106impl Pool {
107 fn resource(self) -> &'static str {
108 match self {
109 Self::Read => "storage read memory",
110 Self::Write => "storage write memory",
111 }
112 }
113
114 fn ceiling(self, inner: &Inner) -> usize {
116 match self {
117 Self::Read => inner.settings.budget.read_memory_bytes.get(),
118 Self::Write => inner.settings.request.max_pending_write_bytes.get() / 2,
119 }
120 }
121
122 fn charge(self, inner: &Inner, state: &mut State, bytes: usize) -> bool {
124 state.collect();
125 let fits = match self {
126 Self::Read => state.status.read_bytes.checked_add(bytes).is_some_and(|total| total <= self.ceiling(inner)),
127 Self::Write => {
128 let capacity = inner.settings.request.max_pending_write_bytes.get();
129 state.scratch_bytes.checked_add(bytes).is_some_and(|total| total <= capacity / 2)
130 && state.status.pending_bytes.checked_add(bytes).is_some_and(|total| total <= capacity)
131 }
132 };
133 if !fits {
134 return false;
135 }
136 match self {
137 Self::Read => state.read_bytes += bytes,
138 Self::Write => state.scratch_bytes += bytes,
139 }
140 state.collect();
141 true
142 }
143
144 fn index(self) -> usize {
145 match self {
146 Self::Read => 0,
147 Self::Write => 1,
148 }
149 }
150
151 fn release(self, state: &mut State, bytes: usize) {
152 match self {
153 Self::Read => state.read_bytes -= bytes,
154 Self::Write => state.scratch_bytes -= bytes,
155 }
156 state.collect();
157 }
158}
159
160impl Refusal {
161 pub(super) fn into_error(self) -> Error {
162 match self {
163 Self::Exceeded(pool) => Error::ResourceExhausted { resource: pool.resource() },
164 Self::Busy => Error::ResourceExhausted { resource: "storage capacity" },
165 Self::Cycle => Error::ResourceExhausted { resource: "storage capacity cycle" },
166 Self::Elapsed => Error::ResourceExhausted { resource: "storage capacity deadline" },
167 Self::Failed(error) => error,
168 }
169 }
170}
171
172impl From<Refusal> for Error {
173 fn from(refusal: Refusal) -> Self {
174 refusal.into_error()
175 }
176}
177
178impl Reservation {
179 pub(super) fn empty(inner: &Arc<Inner>, pool: Pool, held: &Held) -> Self {
180 Self { inner: Arc::clone(inner), pool, bytes: 0, held: held.clone(), lifecycle: false }
181 }
182
183 pub(super) fn charged(inner: &Arc<Inner>, pool: Pool, bytes: usize) -> Self {
184 let held = Held::default();
185 held.0.fetch_add(bytes, Ordering::SeqCst);
186 Self { inner: Arc::clone(inner), pool, bytes, held, lifecycle: false }
187 }
188
189 pub(super) fn charged_lifecycle(inner: &Arc<Inner>, pool: Pool, bytes: usize) -> Self {
190 let mut reservation = Self::charged(inner, pool, bytes);
191 reservation.lifecycle = true;
192 reservation
193 }
194
195 pub(super) fn lifecycle(inner: &Arc<Inner>, pool: Pool, bytes: usize) -> Result<Self> {
196 let mut state = inner.state.lock().unwrap();
197 if bytes > pool.ceiling(inner) || !pool.charge(inner, &mut state, bytes) {
198 return Err(Error::ResourceExhausted { resource: pool.resource() });
199 }
200 drop(state);
201 let held = Held::default();
202 held.0.fetch_add(bytes, Ordering::SeqCst);
203 Ok(Self { inner: Arc::clone(inner), pool, bytes, held, lifecycle: true })
204 }
205
206 pub(super) fn try_new(inner: &Arc<Inner>, pool: Pool, held: &Held, bytes: usize) -> std::result::Result<Self, Refusal> {
207 let mut reservation = Self::empty(inner, pool, held);
208 reservation.try_grow(bytes)?;
209 Ok(reservation)
210 }
211
212 #[tracing::instrument(name = "storage.capacity.wait", level = "trace", skip_all)]
213 pub(super) async fn wait(
214 inner: &Arc<Inner>, pool: Pool, held: &Held, bytes: usize, deadline: Option<Instant>,
215 ) -> std::result::Result<Self, Refusal> {
216 let mut reservation = Self::empty(inner, pool, held);
217 reservation.grow(bytes, deadline).await?;
218 Ok(reservation)
219 }
220
221 pub(super) async fn wait_lifecycle(inner: &Arc<Inner>, pool: Pool, held: &Held, bytes: usize) -> std::result::Result<Self, Refusal> {
222 let mut reservation = Self::empty(inner, pool, held);
223 reservation.lifecycle = true;
224 reservation.grow(bytes, None).await?;
225 Ok(reservation)
226 }
227
228 pub(super) fn bytes(&self) -> usize {
229 self.bytes
230 }
231
232 pub(super) fn try_grow(&mut self, additional: usize) -> std::result::Result<(), Refusal> {
233 if additional == 0 {
234 return Ok(());
235 }
236 let inner = Arc::clone(&self.inner);
237 let mut state = inner.state.lock().unwrap();
238 match self.attempt(&mut state, additional) {
239 Attempt::Ready(()) => Ok(()),
240 Attempt::Wait => Err(Refusal::Busy),
241 Attempt::Refuse(refusal) => Err(refusal),
242 }
243 }
244
245 #[tracing::instrument(name = "storage.capacity.grow", level = "trace", skip_all)]
246 pub(super) async fn grow(&mut self, additional: usize, deadline: Option<Instant>) -> std::result::Result<(), Refusal> {
247 if additional == 0 {
248 return Ok(());
249 }
250 let inner = Arc::clone(&self.inner);
251 let mut waiting: Option<Waiting> = None;
252 let result = inner.wait(deadline, |state| self.grow_step(state, additional, &mut waiting)).await;
253 drop(waiting);
254 result
255 }
256
257 pub(super) async fn grow_shared(owner: &Arc<Mutex<Self>>, bytes: usize) -> std::result::Result<(), Refusal> {
258 let inner = Arc::clone(&owner.lock().unwrap().inner);
259 let mut changed = inner.changed.subscribe();
260 let mut waiting = None;
261 loop {
262 let result = {
263 let mut reservation = owner.lock().unwrap();
264 let additional = bytes.saturating_sub(reservation.bytes);
265 if additional == 0 {
266 Attempt::Ready(())
267 } else {
268 let mut state = inner.state.lock().unwrap();
269 reservation.grow_step(&mut state, additional, &mut waiting)
270 }
271 };
272 match result {
273 Attempt::Ready(()) => return Ok(()),
274 Attempt::Refuse(refusal) => return Err(refusal),
275 Attempt::Wait => {
276 changed.changed().await.map_err(|_| Refusal::Failed(Error::Closing { domain: "storage read memory" }))?;
277 }
278 }
279 }
280 }
281
282 pub(super) fn shrink_to(&mut self, bytes: usize) {
283 if bytes >= self.bytes {
284 return;
285 }
286 let released = self.bytes - bytes;
287 self.bytes = bytes;
288 self.held.0.fetch_sub(released, Ordering::SeqCst);
289 let mut state = self.inner.state.lock().unwrap();
290 self.pool.release(&mut state, released);
291 drop(state);
292 self.inner.notify();
293 }
294
295 pub(super) fn disown(&mut self) -> usize {
296 let bytes = std::mem::take(&mut self.bytes);
297 self.held.0.fetch_sub(bytes, Ordering::SeqCst);
298 bytes
299 }
300
301 pub(super) fn split(&mut self, bytes: usize) -> Self {
302 assert!(bytes <= self.bytes, "split exceeds reservation");
303 self.bytes -= bytes;
304 Self { inner: Arc::clone(&self.inner), pool: self.pool, bytes, held: self.held.clone(), lifecycle: self.lifecycle }
305 }
306
307 pub(super) fn retain(self) -> RetainedMemory {
308 let owner = Arc::new(Mutex::new(self));
309 RetainedMemory::new(owner)
310 }
311
312 fn grow_step(&mut self, state: &mut State, additional: usize, waiting: &mut Option<Waiting>) -> Attempt<()> {
313 match self.attempt(state, additional) {
314 Attempt::Wait if waiting.is_none() && self.held.bytes() != 0 => {
315 match Waiting::join(&self.inner, state, self.pool, &self.held, additional) {
316 Some(joined) => {
317 *waiting = Some(joined);
318 Attempt::Wait
319 }
320 None => Attempt::Refuse(Refusal::Cycle),
321 }
322 }
323 attempt => {
324 if !matches!(&attempt, Attempt::Wait)
325 && let Some(waiting) = waiting
326 {
327 waiting.leave(state);
328 }
329 attempt
330 }
331 }
332 }
333
334 fn attempt(&mut self, state: &mut State, additional: usize) -> Attempt<()> {
335 if self.lifecycle {
336 if state.status.closing || state.status.closed {
337 return Attempt::Refuse(Refusal::Failed(Error::Closing { domain: "storage" }));
338 }
339 } else if let Err(error) = admitting(state, self.pool) {
340 return Attempt::Refuse(Refusal::Failed(error));
341 }
342 let owned = self.held.bytes();
343 if owned.checked_add(additional).is_none_or(|total| total > self.pool.ceiling(&self.inner)) {
344 return Attempt::Refuse(Refusal::Exceeded(self.pool));
345 }
346 if !self.pool.charge(&self.inner, state, additional) {
347 if self.pool == Pool::Write {
348 self.inner.relieve_pressure(state, additional, 0);
349 }
350 return Attempt::Wait;
351 }
352 self.bytes += additional;
353 self.held.0.fetch_add(additional, Ordering::SeqCst);
354 Attempt::Ready(())
355 }
356}
357
358impl Growers {
359 fn can_progress(&self, ceiling: usize, held: usize, need: usize) -> bool {
361 let waiters = self.waiting.values();
362 let held = waiters.clone().fold(held, |total, grower| total.saturating_add(grower.held.bytes()));
363 let free = ceiling.saturating_sub(held);
364 need <= free || waiters.into_iter().any(|grower| grower.need <= free)
365 }
366}
367
368impl Waiting {
369 fn join(inner: &Arc<Inner>, state: &mut State, pool: Pool, held: &Held, need: usize) -> Option<Self> {
370 let growers = &mut state.growers[pool.index()];
371 if !growers.can_progress(pool.ceiling(inner), held.bytes(), need) {
372 return None;
373 }
374 let id = growers.next;
375 growers.next += 1;
376 growers.waiting.insert(id, Grower { held: held.clone(), need });
377 Some(Self { inner: Arc::clone(inner), pool, id, active: true })
378 }
379
380 fn leave(&mut self, state: &mut State) {
383 state.growers[self.pool.index()].waiting.remove(&self.id);
384 self.active = false;
385 }
386}
387
388impl Drop for Waiting {
389 fn drop(&mut self) {
390 if !self.active {
391 return;
392 }
393 self.inner.state.lock().unwrap().growers[self.pool.index()].waiting.remove(&self.id);
394 }
395}
396
397impl Drop for Reservation {
398 fn drop(&mut self) {
399 if self.bytes == 0 {
400 return;
401 }
402 self.held.0.fetch_sub(self.bytes, Ordering::SeqCst);
403 let mut state = self.inner.state.lock().unwrap();
404 self.pool.release(&mut state, self.bytes);
405 drop(state);
406 self.inner.notify();
407 }
408}
409
410impl Inner {
411 #[tracing::instrument(name = "storage.capacity.wait", level = "trace", skip_all)]
412 pub(super) async fn wait<T>(
413 &self, deadline: Option<Instant>, mut attempt: impl FnMut(&mut State) -> Attempt<T>,
414 ) -> std::result::Result<T, Refusal> {
415 let mut changed = self.changed.subscribe();
416 loop {
417 changed.borrow_and_update();
418 match attempt(&mut self.state.lock().unwrap()) {
419 Attempt::Ready(value) => return Ok(value),
420 Attempt::Refuse(refusal) => return Err(refusal),
421 Attempt::Wait => {}
422 }
423 let notified = match deadline {
424 Some(deadline) => tokio::time::timeout_at(deadline, changed.changed()).await.map_err(|_| Refusal::Elapsed)?,
425 None => changed.changed().await,
426 };
427 notified.map_err(|_| Refusal::Failed(Error::Closing { domain: "storage" }))?;
428 }
429 }
430}
431
432impl Inner {
433 #[tracing::instrument(name = "storage.capacity.admit_when", level = "trace", skip_all)]
434 pub(super) async fn admit_when<T>(&self, mut check: impl FnMut(&mut State) -> Result<Option<T>>) -> Result<T> {
435 self.wait(None, |state| match check(state) {
436 Ok(Some(value)) => Attempt::Ready(value),
437 Ok(None) => Attempt::Wait,
438 Err(error) => Attempt::Refuse(Refusal::Failed(error)),
439 })
440 .await
441 .map_err(Refusal::into_error)
442 }
443}
444
445pub(super) fn admitting(state: &State, pool: Pool) -> Result<()> {
450 if state.status.closing {
451 return Err(Error::Closing { domain: "storage" });
452 }
453 if !state.ready || !state.status.running {
454 return Err(Error::NotReady { domain: "storage" });
455 }
456 if pool == Pool::Write
457 && let Some(source) = &state.status.failure
458 {
459 return Err(Error::Persistence { source: source.clone() });
460 }
461 Ok(())
462}
463
464#[cfg(test)]
467mod tests {
468 use super::*;
469 use crate::test_backend::{Data, Recovery, history, storage_settings};
470 use std::num::NonZeroUsize;
471
472 #[test]
473 fn cycle_decision_accounting() {
474 let mut settings = storage_settings();
475 settings.budget.read_memory_bytes = NonZeroUsize::new(100).unwrap();
476 settings.response.batch_bytes = NonZeroUsize::new(40).unwrap();
477 let storage = crate::StorageManager::<()>::new(
478 crate::StorageBackends { primary: Arc::new(Data::<()>::new(history())), state: Arc::new(Recovery::new(history())) },
479 settings,
480 )
481 .unwrap();
482 let inner = &storage.inner;
483 {
484 let mut state = inner.state.lock().unwrap();
485 state.ready = true;
486 state.status.running = true;
487 }
488 let mut a = Reservation::try_new(inner, Pool::Read, &Held::default(), 20).unwrap();
489 let mut b = Reservation::try_new(inner, Pool::Read, &Held::default(), 20).unwrap();
490 let external = Reservation::try_new(inner, Pool::Read, &Held::default(), 60).unwrap();
491 let mut waiting_a = None;
492 let mut waiting_b = None;
493 {
494 let mut state = inner.state.lock().unwrap();
495 assert!(matches!(a.grow_step(&mut state, 40, &mut waiting_a), Attempt::Wait));
496 assert!(matches!(b.grow_step(&mut state, 40, &mut waiting_b), Attempt::Wait));
497 }
498 drop(external);
499 let mut waiting_c = None;
500 let mut c = Reservation::empty(inner, Pool::Read, &Held::default());
501 {
502 let mut state = inner.state.lock().unwrap();
503 assert!(matches!(a.grow_step(&mut state, 40, &mut waiting_a), Attempt::Ready(())));
504 assert!(matches!(c.attempt(&mut state, 10), Attempt::Ready(())));
507 assert!(matches!(c.grow_step(&mut state, 15, &mut waiting_c), Attempt::Wait));
508 }
509 drop((waiting_a, waiting_b, waiting_c));
510 drop((a, b, c));
511 assert_eq!(storage.status().read_bytes, 0);
512 }
513}