exocore_chain/engine/
request_tracker.rs1use std::time::Duration;
2
3use exocore_core::{
4 time::{Clock, Instant},
5 utils::backoff::{BackoffCalculator, BackoffConfig},
6};
7
8pub struct RequestTracker {
11 clock: Clock,
12 backoff_calculator: BackoffCalculator,
13
14 last_request_send: Option<Instant>,
15 last_response_receive: Option<Instant>,
16
17 force_next_request: Option<bool>,
19}
20
21impl RequestTracker {
22 pub fn new_with_clock(clock: Clock, config: RequestTrackerConfig) -> RequestTracker {
23 let backoff_calculator = BackoffCalculator::new(
24 clock.clone(),
25 BackoffConfig {
26 normal_constant: config.min_interval,
27 failure_constant: config.min_interval,
28 failure_exp_base: 2.0,
29 failure_exp_multiplier: Duration::from_secs(5),
30 failure_maximum: config.max_interval,
31 },
32 );
33
34 RequestTracker {
35 clock,
36 backoff_calculator,
37
38 last_request_send: None,
39 last_response_receive: None,
40
41 force_next_request: None,
42 }
43 }
44
45 pub fn set_last_send_now(&mut self) {
46 self.last_request_send = Some(self.clock.instant());
47 }
48
49 pub fn set_last_responded_now(&mut self) {
50 self.last_response_receive = Some(self.clock.instant());
51 self.backoff_calculator.reset();
52 }
53
54 pub fn can_send_request(&mut self) -> bool {
55 if let Some(_force) = self.force_next_request.take() {
56 return true;
57 }
58
59 let should_send_request = self.last_request_send.map_or(true, |send_time| {
60 (self.clock.instant() - send_time) >= self.backoff_calculator.backoff_duration()
61 });
62
63 if should_send_request {
64 if self.last_request_send.is_some() && !self.has_responded_last_request() {
65 self.backoff_calculator.increment_failure();
66 }
67
68 true
69 } else {
70 false
71 }
72 }
73
74 pub fn force_next_request(&mut self) {
75 self.force_next_request = Some(true);
76 }
77
78 pub fn reset(&mut self) {
79 self.last_request_send = None;
80 self.last_response_receive = None;
81 self.force_next_request = None;
82 }
83
84 fn has_responded_last_request(&self) -> bool {
85 matches!((self.last_request_send, self.last_response_receive), (Some(send), Some(resp)) if resp > send)
86 }
87
88 pub fn response_failure_count(&self) -> usize {
89 self.backoff_calculator.consecutive_failures_count() as usize
90 }
91
92 #[cfg(test)]
93 pub fn set_response_failure_count(&mut self, count: usize) {
94 for _ in 0..count {
95 self.backoff_calculator.increment_failure();
96 }
97 }
98}
99
100#[derive(Clone, Copy, Debug)]
102pub struct RequestTrackerConfig {
103 pub min_interval: Duration,
104 pub max_interval: Duration,
105}
106
107impl Default for RequestTrackerConfig {
108 fn default() -> Self {
109 RequestTrackerConfig {
110 min_interval: Duration::from_secs(5),
111 max_interval: Duration::from_secs(30),
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use exocore_core::time::Clock;
119
120 use super::*;
121
122 #[test]
123 fn test_can_send_request_interval() {
124 let mock_clock = Clock::new_mocked();
125 let mut tracker =
126 RequestTracker::new_with_clock(mock_clock.clone(), RequestTrackerConfig::default());
127
128 assert!(tracker.can_send_request());
130
131 tracker.set_last_send_now();
133 assert!(!tracker.can_send_request());
134 assert!(!tracker.has_responded_last_request());
135
136 mock_clock.set_fixed_instant(Instant::now() + Duration::from_millis(5001));
139 assert!(tracker.can_send_request());
140 assert!(!tracker.has_responded_last_request());
141 assert_eq!(tracker.response_failure_count(), 1);
142 }
143
144 #[test]
145 fn test_force_request() {
146 let mock_clock = Clock::new_mocked();
147 let mut tracker =
148 RequestTracker::new_with_clock(mock_clock, RequestTrackerConfig::default());
149
150 tracker.can_send_request();
151 tracker.set_last_send_now();
152
153 assert!(!tracker.can_send_request());
154 tracker.force_next_request();
155 assert!(tracker.can_send_request());
156 }
157
158 #[test]
159 fn test_reset() {
160 let mock_clock = Clock::new_mocked();
161 let mut tracker =
162 RequestTracker::new_with_clock(mock_clock, RequestTrackerConfig::default());
163
164 tracker.can_send_request();
165 tracker.set_last_send_now();
166
167 assert!(!tracker.can_send_request());
168 tracker.reset();
169 assert!(tracker.can_send_request());
170 }
171}