tor_guardmgr/
dirstatus.rs

1//! Types and code to track the readiness status of a directory cache.
2
3use std::time::{Duration, Instant};
4use tor_basic_utils::retry::RetryDelay;
5
6/// Status information about whether a
7/// [`FallbackDir`](crate::fallback::FallbackDir) or
8/// [`Guard`](crate::guard::Guard) is currently usable as a directory cache.
9///
10/// This structure is used to track whether the cache has recently failed, and
11/// if so, when it can be retried.
12#[derive(Debug, Clone)]
13pub(crate) struct DirStatus {
14    /// Used to decide how long to delay before retrying a fallback cache
15    /// that has failed.
16    delay: RetryDelay,
17    /// A time before which we should assume that this fallback cache is broken.
18    ///
19    /// If None, then this fallback cache is ready to use right away.
20    retry_at: Option<Instant>,
21}
22
23impl DirStatus {
24    /// Construct a new DirStatus object with a given lower-bound for delays
25    /// after failure.
26    pub(crate) fn new(delay_floor: Duration) -> Self {
27        DirStatus {
28            delay: RetryDelay::from_duration(delay_floor),
29            retry_at: None,
30        }
31    }
32
33    /// Return true if this `Status` is usable at the time `now`.
34    pub(crate) fn usable_at(&self, now: Instant) -> bool {
35        match self.retry_at {
36            Some(ready) => now >= ready,
37            None => true,
38        }
39    }
40
41    /// Return the time at which this `Status` can next be retried.
42    ///
43    /// A return value of `None`, or of a time in the past, indicates that this
44    /// status can be used immediately.
45    pub(crate) fn next_retriable(&self) -> Option<Instant> {
46        self.retry_at
47    }
48
49    /// Record that the associated fallback directory has been used successfully.
50    ///
51    /// This should only be done after successfully handling a whole reply from the
52    /// directory.
53    pub(crate) fn note_success(&mut self) {
54        self.retry_at = None;
55        self.delay.reset();
56    }
57
58    /// Record that the associated fallback directory has failed.
59    pub(crate) fn note_failure(&mut self, now: Instant) {
60        let mut rng = rand::rng();
61        self.retry_at = Some(now + self.delay.next_delay(&mut rng));
62    }
63}
64
65#[cfg(test)]
66mod test {
67    // @@ begin test lint list maintained by maint/add_warning @@
68    #![allow(clippy::bool_assert_comparison)]
69    #![allow(clippy::clone_on_copy)]
70    #![allow(clippy::dbg_macro)]
71    #![allow(clippy::mixed_attributes_style)]
72    #![allow(clippy::print_stderr)]
73    #![allow(clippy::print_stdout)]
74    #![allow(clippy::single_char_pattern)]
75    #![allow(clippy::unwrap_used)]
76    #![allow(clippy::unchecked_duration_subtraction)]
77    #![allow(clippy::useless_vec)]
78    #![allow(clippy::needless_pass_by_value)]
79    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
80    use super::*;
81
82    #[test]
83    fn status_basics() {
84        let now = Instant::now();
85
86        /// floor to use for testing.
87        const FLOOR: Duration = Duration::from_secs(99);
88
89        let mut status = DirStatus::new(FLOOR);
90        // newly created status is usable.
91        assert!(status.usable_at(now));
92
93        // no longer usable after a failure.
94        status.note_failure(now);
95        assert_eq!(status.next_retriable().unwrap(), now + FLOOR);
96        assert!(!status.usable_at(now));
97
98        // Not enough time has passed.
99        assert!(!status.usable_at(now + FLOOR / 2));
100
101        // Enough time has passed.
102        assert!(status.usable_at(now + FLOOR));
103
104        // Mark as failed again; the timeout will (probably) be longer.
105        status.note_failure(now + FLOOR);
106        assert!(status.next_retriable().unwrap() >= now + FLOOR * 2);
107        assert!(!status.usable_at(now + FLOOR));
108
109        // Mark as succeeded; it should be usable immediately.
110        status.note_success();
111        assert!(status.usable_at(now + FLOOR));
112    }
113}