jellytau_lib/playback_reporting/
throttle.rs1#![allow(dead_code)]
7
8use crate::utils::lock::MutexSafe;
9use std::collections::HashMap;
10use std::sync::{Arc, Mutex};
11use std::time::{Duration, Instant};
12
13pub struct EventThrottler {
18 last_report_time: Arc<Mutex<HashMap<String, Instant>>>,
19 throttle_duration: Duration,
20}
21
22impl EventThrottler {
23 pub fn new() -> Self {
25 Self::with_duration(Duration::from_secs(30))
26 }
27
28 pub fn with_duration(duration: Duration) -> Self {
30 Self {
31 last_report_time: Arc::new(Mutex::new(HashMap::new())),
32 throttle_duration: duration,
33 }
34 }
35
36 pub fn should_report(&self, item_id: &str) -> bool {
38 let last_times = self.last_report_time.lock_safe();
39
40 if let Some(last_time) = last_times.get(item_id) {
41 let elapsed = last_time.elapsed();
42 if elapsed < self.throttle_duration {
43 log::debug!(
44 "[EventThrottler] Skipping report for {}, last reported {:.1}s ago (threshold: {}s)",
45 item_id,
46 elapsed.as_secs_f64(),
47 self.throttle_duration.as_secs()
48 );
49 return false;
50 }
51 }
52
53 true
54 }
55
56 pub fn mark_reported(&self, item_id: &str) {
58 let mut last_times = self.last_report_time.lock_safe();
59 last_times.insert(item_id.to_string(), Instant::now());
60
61 log::debug!(
62 "[EventThrottler] Marked {} as reported at {:?}",
63 item_id,
64 Instant::now()
65 );
66 }
67
68 pub fn clear(&self) {
70 let mut last_times = self.last_report_time.lock_safe();
71 last_times.clear();
72 log::debug!("[EventThrottler] Cleared all tracked report times");
73 }
74
75 pub fn clear_item(&self, item_id: &str) {
77 let mut last_times = self.last_report_time.lock_safe();
78 last_times.remove(item_id);
79 log::debug!("[EventThrottler] Cleared tracking for {}", item_id);
80 }
81}
82
83impl Default for EventThrottler {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89impl Clone for EventThrottler {
90 fn clone(&self) -> Self {
91 Self {
92 last_report_time: Arc::clone(&self.last_report_time),
93 throttle_duration: self.throttle_duration,
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use std::thread;
102
103 #[test]
104 fn test_throttler_allows_first_report() {
105 let throttler = EventThrottler::new();
106 assert!(throttler.should_report("item1"));
107 }
108
109 #[test]
110 fn test_throttler_blocks_immediate_second_report() {
111 let throttler = EventThrottler::new();
112 assert!(throttler.should_report("item1"));
113 throttler.mark_reported("item1");
114 assert!(!throttler.should_report("item1"));
115 }
116
117 #[test]
118 fn test_throttler_allows_report_after_duration() {
119 let throttler = EventThrottler::with_duration(Duration::from_millis(100));
120 assert!(throttler.should_report("item1"));
121 throttler.mark_reported("item1");
122 assert!(!throttler.should_report("item1"));
123
124 thread::sleep(Duration::from_millis(150));
125 assert!(throttler.should_report("item1"));
126 }
127
128 #[test]
129 fn test_throttler_handles_multiple_items() {
130 let throttler = EventThrottler::new();
131 assert!(throttler.should_report("item1"));
132 throttler.mark_reported("item1");
133
134 assert!(throttler.should_report("item2"));
135 throttler.mark_reported("item2");
136
137 assert!(!throttler.should_report("item1"));
138 assert!(!throttler.should_report("item2"));
139 }
140
141 #[test]
142 fn test_throttler_clear() {
143 let throttler = EventThrottler::new();
144 throttler.mark_reported("item1");
145 assert!(!throttler.should_report("item1"));
146
147 throttler.clear();
148 assert!(throttler.should_report("item1"));
149 }
150
151 #[test]
152 fn test_throttler_clear_item() {
153 let throttler = EventThrottler::new();
154 throttler.mark_reported("item1");
155 throttler.mark_reported("item2");
156
157 assert!(!throttler.should_report("item1"));
158 assert!(!throttler.should_report("item2"));
159
160 throttler.clear_item("item1");
161 assert!(throttler.should_report("item1"));
162 assert!(!throttler.should_report("item2"));
163 }
164
165 #[test]
166 fn test_throttler_clone_shares_state() {
167 let throttler1 = EventThrottler::new();
168 throttler1.mark_reported("item1");
169
170 let throttler2 = throttler1.clone();
171 assert!(!throttler2.should_report("item1"));
172
173 throttler2.mark_reported("item2");
174 assert!(!throttler1.should_report("item2"));
175 }
176}