1use std::{collections::VecDeque, fmt, time::Duration};
7
8use parking_lot::Mutex;
9use tokio::{
10 sync::Notify,
11 time::{Instant, sleep},
12};
13
14use crate::Error;
15
16const DEFAULT_HISTORY_REQUESTS: usize = 50;
17const DEFAULT_HISTORY_WINDOW: Duration = Duration::from_secs(30);
18const DEFAULT_GENERAL_REQUESTS: usize = 200;
19const DEFAULT_GENERAL_WINDOW: Duration = Duration::from_mins(1);
20const MAX_INITIAL_CAPACITY: usize = 1_024;
21pub(crate) const MAX_COOLDOWN: Duration = Duration::from_hours(24);
33
34#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
36#[non_exhaustive]
37pub enum RateLimitKind {
38 History,
40 General,
42}
43
44impl fmt::Display for RateLimitKind {
45 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 Self::History => formatter.write_str("history"),
48 Self::General => formatter.write_str("general"),
49 }
50 }
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55#[non_exhaustive]
56pub struct RateLimit {
57 max_requests: usize,
58 window: Duration,
59}
60
61impl RateLimit {
62 pub fn new(max_requests: usize, window: Duration) -> Result<Self, Error> {
69 if max_requests == 0
70 || window.is_zero()
71 || std::time::Instant::now().checked_add(window).is_none()
72 {
73 return Err(Error::Configuration(
74 "rate limits require a positive request count and representable window".to_owned(),
75 ));
76 }
77 Ok(Self {
78 max_requests,
79 window,
80 })
81 }
82
83 const fn trusted(max_requests: usize, window: Duration) -> Self {
84 Self {
85 max_requests,
86 window,
87 }
88 }
89
90 #[must_use]
92 pub const fn max_requests(self) -> usize {
93 self.max_requests
94 }
95
96 #[must_use]
98 pub const fn window(self) -> Duration {
99 self.window
100 }
101}
102
103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108#[non_exhaustive]
109pub struct RateLimitConfig {
110 history: RateLimit,
111 general: RateLimit,
112 exit_reserve: usize,
113}
114
115impl RateLimitConfig {
116 #[must_use]
118 pub const fn new(history: RateLimit, general: RateLimit) -> Self {
119 Self {
120 history,
121 general,
122 exit_reserve: 0,
123 }
124 }
125
126 pub fn with_exit_reserve(mut self, reserve: usize) -> Result<Self, Error> {
137 if reserve >= self.general.max_requests {
138 return Err(Error::Configuration(
139 "exit reserve must be smaller than the general request limit".to_owned(),
140 ));
141 }
142 self.exit_reserve = reserve;
143 Ok(self)
144 }
145
146 #[must_use]
148 pub const fn exit_reserve(self) -> usize {
149 self.exit_reserve
150 }
151
152 #[must_use]
154 pub const fn history(self) -> RateLimit {
155 self.history
156 }
157
158 #[must_use]
160 pub const fn general(self) -> RateLimit {
161 self.general
162 }
163
164 pub(crate) const fn limit(self, kind: RateLimitKind) -> RateLimit {
165 match kind {
166 RateLimitKind::History => self.history,
167 RateLimitKind::General => self.general,
168 }
169 }
170}
171
172impl Default for RateLimitConfig {
173 fn default() -> Self {
174 Self {
175 history: RateLimit::trusted(DEFAULT_HISTORY_REQUESTS, DEFAULT_HISTORY_WINDOW),
176 general: RateLimit::trusted(DEFAULT_GENERAL_REQUESTS, DEFAULT_GENERAL_WINDOW),
177 exit_reserve: 0,
178 }
179 }
180}
181
182pub(crate) struct RateLimits {
183 config: Option<RateLimitConfig>,
184 history: Option<WindowLimiter>,
185 general: Option<WindowLimiter>,
186}
187
188impl RateLimits {
189 pub(crate) fn new(config: Option<RateLimitConfig>) -> Self {
190 let history = config.map(|limits| WindowLimiter::new(limits.history, 0));
191 let general = config.map(|limits| WindowLimiter::new(limits.general, limits.exit_reserve));
192 Self {
193 config,
194 history,
195 general,
196 }
197 }
198
199 pub(crate) fn config(&self) -> Option<RateLimitConfig> {
200 self.config.map(|mut config| {
201 if let Some(limiter) = &self.general {
202 config.exit_reserve = limiter.state.lock().exit_reserve;
203 }
204 config
205 })
206 }
207
208 pub(crate) fn set_exit_reserve(&self, reserve: usize) -> Result<(), Error> {
209 let limiter = self.general.as_ref().ok_or_else(|| {
210 Error::Configuration("exit reserve requires enabled rate limits".to_owned())
211 })?;
212 let mut state = limiter.state.lock();
213 if reserve >= limiter.limit.max_requests {
214 return Err(Error::Configuration(
215 "exit reserve must be smaller than the general request limit".to_owned(),
216 ));
217 }
218 let changed = state.exit_reserve != reserve;
219 state.exit_reserve = reserve;
220 drop(state);
221 if changed {
222 limiter.policy_changed.notify_waiters();
223 }
224 Ok(())
225 }
226
227 pub(crate) fn limit(&self, kind: RateLimitKind) -> RateLimit {
228 self.config.unwrap_or_default().limit(kind)
229 }
230
231 pub(crate) async fn wait(&self, kind: RateLimitKind) {
232 let Some(limiter) = self.limiter(kind) else {
233 return;
234 };
235 loop {
236 let changed = limiter.policy_changed.notified();
237 tokio::pin!(changed);
238 changed.as_mut().enable();
239 match limiter.try_acquire(false) {
240 Ok(()) => return,
241 Err(retry_after) => {
242 tokio::select! {
243 () = &mut changed => {},
244 () = sleep(retry_after) => {},
245 }
246 }
247 }
248 }
249 }
250
251 pub(crate) fn try_acquire(&self, kind: RateLimitKind) -> Result<(), Duration> {
252 self.limiter(kind)
253 .map_or(Ok(()), |limiter| limiter.try_acquire(false))
254 }
255
256 pub(crate) fn try_acquire_exit(&self) -> Result<(), Duration> {
257 self.general
258 .as_ref()
259 .map_or(Ok(()), |limiter| limiter.try_acquire(true))
260 }
261
262 pub(crate) fn cool_down(&self, kind: RateLimitKind, retry_after: Duration) {
263 if let Some(limiter) = self.limiter(kind) {
264 limiter.cool_down(retry_after);
265 }
266 }
267
268 fn limiter(&self, kind: RateLimitKind) -> Option<&WindowLimiter> {
269 match kind {
270 RateLimitKind::History => self.history.as_ref(),
271 RateLimitKind::General => self.general.as_ref(),
272 }
273 }
274}
275
276struct WindowLimiter {
277 limit: RateLimit,
278 policy_changed: Notify,
279 state: Mutex<WindowState>,
280}
281
282impl WindowLimiter {
283 fn new(limit: RateLimit, exit_reserve: usize) -> Self {
284 Self {
285 limit,
286 policy_changed: Notify::new(),
287 state: Mutex::new(WindowState::with_capacity(
288 limit.max_requests.min(MAX_INITIAL_CAPACITY),
289 exit_reserve,
290 )),
291 }
292 }
293
294 fn try_acquire(&self, exit: bool) -> Result<(), Duration> {
295 let mut state = self.state.lock();
296 let capacity = if exit {
297 self.limit.max_requests
298 } else {
299 self.limit.max_requests - state.exit_reserve
300 };
301 let now = Instant::now();
302 state.expire(now, self.limit.window);
303 let retry_after = state.retry_after(now, self.limit.window, capacity);
304 if retry_after.is_zero() {
305 state.admitted.push_back(now);
306 Ok(())
307 } else {
308 Err(retry_after)
309 }
310 }
311
312 fn cool_down(&self, retry_after: Duration) {
313 let mut state = self.state.lock();
314 let deadline = saturating_deadline(Instant::now(), retry_after.min(MAX_COOLDOWN));
315 if state
316 .cooldown_until
317 .is_none_or(|current| deadline > current)
318 {
319 state.cooldown_until = Some(deadline);
320 }
321 }
322}
323
324struct WindowState {
325 exit_reserve: usize,
326 admitted: VecDeque<Instant>,
327 cooldown_until: Option<Instant>,
328}
329
330impl WindowState {
331 fn with_capacity(capacity: usize, exit_reserve: usize) -> Self {
332 Self {
333 exit_reserve,
334 admitted: VecDeque::with_capacity(capacity),
335 cooldown_until: None,
336 }
337 }
338
339 fn expire(&mut self, now: Instant, window: Duration) {
340 while self
341 .admitted
342 .front()
343 .is_some_and(|admitted| now.saturating_duration_since(*admitted) >= window)
344 {
345 self.admitted.pop_front();
346 }
347 if self.cooldown_until.is_some_and(|deadline| deadline <= now) {
348 self.cooldown_until = None;
349 }
350 }
351
352 fn retry_after(&self, now: Instant, window: Duration, capacity: usize) -> Duration {
353 let cooldown = self.cooldown_until.map_or(Duration::ZERO, |deadline| {
354 deadline.saturating_duration_since(now)
355 });
356 let capacity_delay = if self.admitted.len() < capacity {
357 Duration::ZERO
358 } else {
359 self.admitted
362 .get(self.admitted.len() - capacity)
363 .map_or(window, |admitted| {
364 admitted
365 .checked_add(window)
366 .map_or(window, |deadline| deadline.saturating_duration_since(now))
367 })
368 };
369 cooldown.max(capacity_delay)
370 }
371}
372
373fn saturating_deadline(now: Instant, duration: Duration) -> Instant {
378 if let Some(deadline) = now.checked_add(duration) {
379 return deadline;
380 }
381 let mut low = 0_u64;
391 let mut high = u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX);
392 let mut deadline = now;
393 while high - low > 1 {
394 let mid = u64::midpoint(low, high);
395 if let Some(probe) = now.checked_add(Duration::from_nanos(mid)) {
396 low = mid;
397 deadline = probe;
398 } else {
399 high = mid;
400 }
401 }
402 deadline
403}
404
405#[cfg(test)]
406mod tests {
407 use std::sync::Arc;
408
409 use tokio::time::{Instant, advance};
410
411 use super::*;
412
413 fn limits(requests: usize, window: Duration) -> Arc<RateLimits> {
414 let limit = RateLimit::new(requests, window)
415 .unwrap_or_else(|error| panic!("test rate limit must be valid: {error}"));
416 Arc::new(RateLimits::new(Some(RateLimitConfig::new(limit, limit))))
417 }
418
419 #[tokio::test(start_paused = true)]
420 async fn wait_resumes_when_the_rolling_window_releases_capacity() {
421 let window = Duration::from_secs(10);
422 let limits = limits(1, window);
423 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
424
425 let started = Instant::now();
426 let waiting_limits = Arc::clone(&limits);
427 let waiter = tokio::spawn(async move {
428 waiting_limits.wait(RateLimitKind::General).await;
429 Instant::now()
430 });
431 tokio::task::yield_now().await;
432 assert!(!waiter.is_finished());
433
434 advance(window).await;
435 let admitted = waiter
436 .await
437 .unwrap_or_else(|error| panic!("waiting task must finish: {error}"));
438 assert_eq!(admitted.saturating_duration_since(started), window);
439 }
440
441 #[tokio::test(start_paused = true)]
442 async fn provider_cooldown_blocks_admission_until_its_deadline() {
443 let cooldown = Duration::from_secs(15);
444 let limits = limits(2, Duration::from_secs(30));
445 limits.cool_down(RateLimitKind::General, cooldown);
446
447 assert_eq!(limits.try_acquire(RateLimitKind::General), Err(cooldown));
448 advance(cooldown).await;
449 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
450 }
451
452 #[tokio::test(start_paused = true)]
453 async fn an_oversized_cooldown_is_clamped_not_skipped() {
454 let limits = limits(2, Duration::from_secs(30));
455 limits.cool_down(RateLimitKind::General, Duration::MAX);
456
457 assert_eq!(
458 limits.try_acquire(RateLimitKind::General),
459 Err(Duration::from_hours(24)),
460 "an unrepresentable provider cooldown must still block admission"
461 );
462 }
463
464 #[test]
465 fn an_unrepresentable_deadline_search_terminates_and_still_blocks() {
466 let now = Instant::now();
467 let deadline = saturating_deadline(now, Duration::MAX);
468 let offset = deadline.saturating_duration_since(now);
472 assert!(
473 offset > Duration::from_hours(24) && offset <= Duration::from_nanos(u64::MAX),
474 "the saturated deadline must still block admission within the \
475 searched nanosecond domain, found {offset:?}"
476 );
477 }
478
479 #[tokio::test(start_paused = true)]
480 async fn rolling_window_expires_staggered_attempts_individually() {
481 let window = Duration::from_secs(10);
482 let limits = limits(2, window);
483 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
484 advance(Duration::from_secs(5)).await;
485 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
486
487 advance(Duration::from_secs(5)).await;
488 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
489 assert_eq!(
490 limits.try_acquire(RateLimitKind::General),
491 Err(Duration::from_secs(5))
492 );
493 }
494
495 #[tokio::test(start_paused = true)]
496 async fn cancelled_waiter_leaves_no_ghost_reservation() {
497 let window = Duration::from_secs(10);
498 let limits = limits(1, window);
499 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
500 let waiting_limits = Arc::clone(&limits);
501 let waiter = tokio::spawn(async move {
502 waiting_limits.wait(RateLimitKind::General).await;
503 });
504 tokio::task::yield_now().await;
505 waiter.abort();
506
507 advance(window).await;
508 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
509 }
510
511 #[tokio::test(start_paused = true)]
512 async fn default_history_budget_reaches_its_limit_before_throttling() {
513 let limits = Arc::new(RateLimits::new(Some(RateLimitConfig::default())));
514 for request_number in 1..=50 {
515 assert_eq!(
516 limits.try_acquire(RateLimitKind::History),
517 Ok(()),
518 "history request {request_number} must be admitted"
519 );
520 }
521 assert_eq!(
522 limits.try_acquire(RateLimitKind::History),
523 Err(Duration::from_secs(30))
524 );
525 }
526
527 #[tokio::test(start_paused = true)]
528 async fn default_general_budget_reaches_its_limit_before_throttling() {
529 let limits = Arc::new(RateLimits::new(Some(RateLimitConfig::default())));
530 for request_number in 1..=200 {
531 assert_eq!(
532 limits.try_acquire(RateLimitKind::General),
533 Ok(()),
534 "general request {request_number} must be admitted"
535 );
536 }
537 assert_eq!(
538 limits.try_acquire(RateLimitKind::General),
539 Err(Duration::from_mins(1))
540 );
541 }
542
543 #[tokio::test(start_paused = true)]
544 async fn later_provider_cooldown_cannot_be_shortened() {
545 let limits = limits(200, Duration::from_mins(1));
546 limits.cool_down(RateLimitKind::General, Duration::from_secs(30));
547 limits.cool_down(RateLimitKind::General, Duration::from_secs(5));
548 assert_eq!(
549 limits.try_acquire(RateLimitKind::General),
550 Err(Duration::from_secs(30))
551 );
552 }
553
554 #[tokio::test(start_paused = true)]
555 async fn admission_uses_the_later_of_window_and_provider_cooldown() {
556 let limits = limits(1, Duration::from_secs(20));
557 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
558 limits.cool_down(RateLimitKind::General, Duration::from_secs(5));
559 assert_eq!(
560 limits.try_acquire(RateLimitKind::General),
561 Err(Duration::from_secs(20))
562 );
563 }
564
565 #[test]
566 fn history_and_general_budgets_are_independent() {
567 let limits = limits(1, Duration::from_secs(30));
568 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
569 assert_eq!(limits.try_acquire(RateLimitKind::History), Ok(()));
570 assert!(limits.try_acquire(RateLimitKind::General).is_err());
571 assert!(limits.try_acquire(RateLimitKind::History).is_err());
572 }
573
574 #[test]
575 fn public_defaults_match_the_provider_contract() {
576 let config = RateLimitConfig::default();
577 assert_eq!(config.history().max_requests(), 50);
578 assert_eq!(config.history().window(), Duration::from_secs(30));
579 assert_eq!(config.general().max_requests(), 200);
580 assert_eq!(config.general().window(), Duration::from_mins(1));
581 }
582
583 #[test]
584 fn public_rate_limit_rejects_zero_values() {
585 assert!(RateLimit::new(0, Duration::from_secs(1)).is_err());
586 assert!(RateLimit::new(1, Duration::ZERO).is_err());
587 }
588
589 fn reserved_limits() -> Arc<RateLimits> {
590 let config = RateLimitConfig::new(
591 RateLimit::new(5, Duration::from_secs(10)).unwrap_or_else(|error| panic!("{error}")),
592 RateLimit::new(5, Duration::from_secs(10)).unwrap_or_else(|error| panic!("{error}")),
593 )
594 .with_exit_reserve(2)
595 .unwrap_or_else(|error| panic!("{error}"));
596 Arc::new(RateLimits::new(Some(config)))
597 }
598
599 #[tokio::test(start_paused = true)]
600 async fn exit_reserve_keeps_ordinary_waiters_below_the_shared_aggregate_cap() {
601 let limits = reserved_limits();
602 for _ in 0..3 {
603 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
604 }
605 assert_eq!(
606 limits.try_acquire(RateLimitKind::General),
607 Err(Duration::from_secs(10))
608 );
609 let waiting_limits = Arc::clone(&limits);
610 let waiter = tokio::spawn(async move {
611 waiting_limits.wait(RateLimitKind::General).await;
612 });
613 tokio::task::yield_now().await;
614 assert!(!waiter.is_finished());
615 assert_eq!(limits.try_acquire_exit(), Ok(()));
616 tokio::task::yield_now().await;
617 assert!(!waiter.is_finished());
618 assert_eq!(limits.try_acquire_exit(), Ok(()));
619 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(10)));
620 waiter.abort();
621 assert!(waiter.await.is_err_and(|error| error.is_cancelled()));
622 advance(Duration::from_secs(10)).await;
623 for _ in 0..3 {
624 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
625 }
626 assert_eq!(
627 limits.try_acquire(RateLimitKind::General),
628 Err(Duration::from_secs(10))
629 );
630 assert_eq!(limits.try_acquire_exit(), Ok(()));
631 }
632
633 #[tokio::test(start_paused = true)]
634 async fn ordinary_retry_after_waits_for_enough_aggregate_entries_to_expire() {
635 let limits = reserved_limits();
636 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
637 advance(Duration::from_secs(1)).await;
638 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
639 advance(Duration::from_secs(1)).await;
640 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
641 advance(Duration::from_secs(1)).await;
642 assert_eq!(limits.try_acquire_exit(), Ok(()));
643 advance(Duration::from_secs(1)).await;
644 assert_eq!(limits.try_acquire_exit(), Ok(()));
645 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(6)));
646 assert_eq!(
647 limits.try_acquire(RateLimitKind::General),
648 Err(Duration::from_secs(8))
649 );
650 advance(Duration::from_secs(6)).await;
651 assert_eq!(
652 limits.try_acquire(RateLimitKind::General),
653 Err(Duration::from_secs(2))
654 );
655 advance(Duration::from_secs(2)).await;
656 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
657 assert_eq!(
658 limits.try_acquire(RateLimitKind::General),
659 Err(Duration::from_secs(1))
660 );
661 }
662
663 #[tokio::test(start_paused = true)]
664 async fn concurrent_exit_admission_never_exceeds_shared_capacity() {
665 let limits = reserved_limits();
666 for _ in 0..3 {
667 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
668 }
669 let mut tasks = tokio::task::JoinSet::new();
670 for _ in 0..20 {
671 let clone = Arc::clone(&limits);
672 tasks.spawn(async move { clone.try_acquire_exit() });
673 }
674 let mut accepted = 0;
675 let mut refused = 0;
676 while let Some(result) = tasks.join_next().await {
677 match result.unwrap_or_else(|error| panic!("{error}")) {
678 Ok(()) => accepted += 1,
679 Err(delay) => {
680 assert_eq!(delay, Duration::from_secs(10));
681 refused += 1;
682 }
683 }
684 }
685 assert_eq!(accepted, 2);
686 assert_eq!(refused, 18);
687 }
688
689 #[tokio::test(start_paused = true)]
690 async fn provider_cooldown_blocks_ordinary_and_reserved_capacity() {
691 let limits = reserved_limits();
692 limits.cool_down(RateLimitKind::General, Duration::from_secs(15));
693 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(15)));
694 assert_eq!(
695 limits.try_acquire(RateLimitKind::General),
696 Err(Duration::from_secs(15))
697 );
698 advance(Duration::from_secs(14)).await;
699 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(1)));
700 assert_eq!(
701 limits.try_acquire(RateLimitKind::General),
702 Err(Duration::from_secs(1))
703 );
704 advance(Duration::from_secs(1)).await;
705 assert_eq!(limits.try_acquire_exit(), Ok(()));
706 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
707 }
708
709 #[test]
710 fn exit_reserve_retains_ordinary_capacity_and_defaults_to_zero() {
711 let config = RateLimitConfig::default();
712 assert_eq!(config.exit_reserve(), 0);
713 assert_eq!(
714 RateLimitConfig::new(config.history(), config.general()).exit_reserve(),
715 0
716 );
717 assert_eq!(
718 config
719 .with_exit_reserve(199)
720 .unwrap_or_else(|error| panic!("{error}"))
721 .exit_reserve(),
722 199
723 );
724 assert!(matches!(
725 config.with_exit_reserve(200),
726 Err(Error::Configuration(_))
727 ));
728 assert!(matches!(
729 config.with_exit_reserve(usize::MAX),
730 Err(Error::Configuration(_))
731 ));
732 }
733
734 #[tokio::test(start_paused = true)]
735 async fn runtime_release_wakes_all_registered_waiters_without_advancing_time() {
736 let limits = reserved_limits();
737 for _ in 0..3 {
738 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
739 }
740 let first = limits.wait(RateLimitKind::General);
741 let second = limits.wait(RateLimitKind::General);
742 tokio::pin!(first, second);
743 assert!(futures_util::poll!(first.as_mut()).is_pending());
744 assert!(futures_util::poll!(second.as_mut()).is_pending());
745 let started = Instant::now();
746 let clone = Arc::clone(&limits);
747 clone
748 .set_exit_reserve(0)
749 .unwrap_or_else(|error| panic!("{error}"));
750 assert!(futures_util::poll!(first.as_mut()).is_ready());
751 assert!(futures_util::poll!(second.as_mut()).is_ready());
752 assert_eq!(Instant::now(), started);
753 assert_eq!(limits.config().map(RateLimitConfig::exit_reserve), Some(0));
754 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(10)));
755 }
756
757 #[tokio::test(start_paused = true)]
758 async fn runtime_growth_rechecks_waiters_and_preserves_spent_capacity() {
759 let limits = reserved_limits();
760 for _ in 0..3 {
761 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
762 }
763 let read = limits.wait(RateLimitKind::General);
764 tokio::pin!(read);
765 assert!(futures_util::poll!(read.as_mut()).is_pending());
766 limits
767 .set_exit_reserve(0)
768 .unwrap_or_else(|error| panic!("{error}"));
769 limits
770 .set_exit_reserve(4)
771 .unwrap_or_else(|error| panic!("{error}"));
772 assert!(futures_util::poll!(read.as_mut()).is_pending());
773 assert_eq!(limits.config().map(RateLimitConfig::exit_reserve), Some(4));
774 assert_eq!(
775 limits.try_acquire(RateLimitKind::General),
776 Err(Duration::from_secs(10))
777 );
778 assert_eq!(limits.try_acquire_exit(), Ok(()));
779 assert_eq!(limits.try_acquire_exit(), Ok(()));
780 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(10)));
781 limits
782 .set_exit_reserve(0)
783 .unwrap_or_else(|error| panic!("{error}"));
784 limits
785 .set_exit_reserve(4)
786 .unwrap_or_else(|error| panic!("{error}"));
787 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(10)));
788 limits
789 .set_exit_reserve(0)
790 .unwrap_or_else(|error| panic!("{error}"));
791 assert!(futures_util::poll!(read.as_mut()).is_pending());
792 advance(Duration::from_secs(10)).await;
793 assert!(futures_util::poll!(read.as_mut()).is_ready());
794 }
795
796 #[tokio::test(start_paused = true)]
797 async fn runtime_changes_preserve_timestamps_cooldown_and_invalid_policy_state() {
798 let limits = reserved_limits();
799 assert_eq!(limits.try_acquire_exit(), Ok(()));
800 advance(Duration::from_secs(2)).await;
801 assert_eq!(limits.try_acquire_exit(), Ok(()));
802 limits.cool_down(RateLimitKind::General, Duration::from_secs(3));
803 for reserve in [4, 0, 2] {
804 limits
805 .set_exit_reserve(reserve)
806 .unwrap_or_else(|error| panic!("{error}"));
807 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(3)));
808 }
809 for reserve in [5, usize::MAX] {
810 assert!(matches!(
811 limits.set_exit_reserve(reserve),
812 Err(Error::Configuration(_))
813 ));
814 assert_eq!(limits.config().map(RateLimitConfig::exit_reserve), Some(2));
815 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(3)));
816 }
817 advance(Duration::from_secs(3)).await;
818 for _ in 0..3 {
819 assert_eq!(limits.try_acquire_exit(), Ok(()));
820 }
821 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(5)));
822 advance(Duration::from_secs(5)).await;
823 assert_eq!(limits.try_acquire_exit(), Ok(()));
824 assert_eq!(limits.try_acquire_exit(), Err(Duration::from_secs(2)));
825 }
826
827 #[tokio::test(start_paused = true)]
828 async fn runtime_waiter_rechecks_each_replacement_including_release_then_growth() {
829 let limits = reserved_limits();
830 limits
831 .set_exit_reserve(4)
832 .unwrap_or_else(|error| panic!("{error}"));
833 assert_eq!(limits.try_acquire(RateLimitKind::General), Ok(()));
834 let read = limits.wait(RateLimitKind::General);
835 tokio::pin!(read);
836 assert!(futures_util::poll!(read.as_mut()).is_pending());
837 limits
838 .set_exit_reserve(0)
839 .unwrap_or_else(|error| panic!("{error}"));
840 limits
841 .set_exit_reserve(4)
842 .unwrap_or_else(|error| panic!("{error}"));
843 assert!(futures_util::poll!(read.as_mut()).is_pending());
844 limits
845 .set_exit_reserve(3)
846 .unwrap_or_else(|error| panic!("{error}"));
847 assert!(futures_util::poll!(read.as_mut()).is_ready());
848 assert_eq!(
849 limits.try_acquire(RateLimitKind::General),
850 Err(Duration::from_secs(10))
851 );
852 }
853}