tracing_throttle/application/
registry.rs1use crate::application::ports::{Clock, Storage};
7use crate::domain::{policy::Policy, signature::EventSignature, summary::SuppressionCounter};
8
9#[cfg(feature = "human-readable")]
10use crate::domain::metadata::EventMetadata;
11use std::sync::Arc;
12use std::time::Instant;
13
14#[derive(Debug, Clone)]
16pub struct EventState {
17 pub policy: Policy,
19 pub counter: SuppressionCounter,
21 #[cfg(feature = "human-readable")]
23 pub metadata: Option<EventMetadata>,
24}
25
26impl EventState {
27 pub fn new(policy: Policy, initial_timestamp: Instant) -> Self {
29 Self {
30 policy,
31 counter: SuppressionCounter::new(initial_timestamp),
32 #[cfg(feature = "human-readable")]
33 metadata: None,
34 }
35 }
36
37 #[cfg(feature = "human-readable")]
39 pub fn new_with_metadata(
40 policy: Policy,
41 initial_timestamp: Instant,
42 metadata: EventMetadata,
43 ) -> Self {
44 Self {
45 policy,
46 counter: SuppressionCounter::new(initial_timestamp),
47 metadata: Some(metadata),
48 }
49 }
50
51 #[cfg(feature = "human-readable")]
55 pub fn set_metadata(&mut self, metadata: EventMetadata) {
56 if self.metadata.is_none() {
57 self.metadata = Some(metadata);
58 }
59 }
60
61 #[cfg(feature = "redis-storage")]
65 pub fn from_snapshot(
66 policy: Policy,
67 suppressed_count: usize,
68 first_suppressed: Instant,
69 last_suppressed: Instant,
70 ) -> Self {
71 Self::from_snapshot_with_reported(
72 policy,
73 suppressed_count,
74 0,
75 first_suppressed,
76 last_suppressed,
77 first_suppressed,
78 )
79 }
80
81 #[cfg(feature = "redis-storage")]
85 pub fn from_snapshot_with_reported(
86 policy: Policy,
87 suppressed_count: usize,
88 reported_count: usize,
89 first_suppressed: Instant,
90 last_suppressed: Instant,
91 last_reported: Instant,
92 ) -> Self {
93 let first_unreported = if reported_count == 0 {
94 first_suppressed
95 } else {
96 last_reported
97 };
98
99 Self::from_snapshot_with_reported_and_first_unreported(
100 policy,
101 suppressed_count,
102 reported_count,
103 first_suppressed,
104 last_suppressed,
105 last_reported,
106 first_unreported,
107 )
108 }
109
110 #[cfg(feature = "redis-storage")]
114 pub fn from_snapshot_with_reported_and_first_unreported(
115 policy: Policy,
116 suppressed_count: usize,
117 reported_count: usize,
118 first_suppressed: Instant,
119 last_suppressed: Instant,
120 last_reported: Instant,
121 first_unreported: Instant,
122 ) -> Self {
123 Self {
124 policy,
125 counter: SuppressionCounter::from_snapshot_with_reported_and_first_unreported(
126 suppressed_count,
127 reported_count,
128 first_suppressed,
129 last_suppressed,
130 last_reported,
131 first_unreported,
132 ),
133 metadata: None,
134 }
135 }
136}
137
138#[derive(Clone)]
145pub struct SuppressionRegistry<S>
146where
147 S: Storage<EventSignature, EventState> + Clone,
148{
149 storage: S,
150 clock: Arc<dyn Clock>,
151 default_policy: Policy,
152}
153
154impl<S> SuppressionRegistry<S>
155where
156 S: Storage<EventSignature, EventState> + Clone,
157{
158 pub fn new(storage: S, clock: Arc<dyn Clock>, default_policy: Policy) -> Self {
162 Self {
163 storage,
164 clock,
165 default_policy,
166 }
167 }
168
169 pub fn with_event_state<F, R>(&self, signature: EventSignature, f: F) -> R
175 where
176 F: FnOnce(&mut EventState, Instant) -> R,
177 {
178 let now = self.clock.now();
179 let default_policy = self.default_policy.clone();
180 self.storage.with_entry_mut(
181 signature,
182 || EventState::new(default_policy, now),
183 |state| f(state, now),
184 )
185 }
186
187 pub fn default_policy(&self) -> &Policy {
189 &self.default_policy
190 }
191
192 pub fn clone_default_policy(&self) -> Policy {
194 self.default_policy.clone()
195 }
196
197 pub fn len(&self) -> usize {
199 self.storage.len()
200 }
201
202 pub fn is_empty(&self) -> bool {
204 self.storage.is_empty()
205 }
206
207 pub fn clear(&self) {
209 self.storage.clear();
210 }
211
212 pub fn for_each<F>(&self, f: F)
214 where
215 F: FnMut(&EventSignature, &EventState),
216 {
217 self.storage.for_each(f);
218 }
219
220 pub fn cleanup<F>(&self, f: F)
222 where
223 F: FnMut(&EventSignature, &mut EventState) -> bool,
224 {
225 self.storage.retain(f);
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use crate::domain::policy::Policy;
233 use crate::infrastructure::clock::SystemClock;
234 use crate::infrastructure::storage::ShardedStorage;
235 use std::sync::Arc;
236
237 #[test]
238 fn test_registry_creation() {
239 let storage = Arc::new(ShardedStorage::new());
240 let clock = Arc::new(SystemClock::new());
241 let policy = Policy::count_based(100).unwrap();
242 let registry = SuppressionRegistry::new(storage, clock, policy);
243
244 assert_eq!(registry.len(), 0);
245 assert!(registry.is_empty());
246 }
247
248 #[test]
249 fn test_with_event_state() {
250 let storage = Arc::new(ShardedStorage::new());
251 let clock = Arc::new(SystemClock::new());
252 let policy = Policy::count_based(100).unwrap();
253 let registry = SuppressionRegistry::new(storage, clock, policy);
254 let sig = EventSignature::simple("INFO", "Test message");
255
256 registry.with_event_state(sig, |state, _now| {
258 assert_eq!(state.counter.count(), 0);
259 });
260
261 assert_eq!(registry.len(), 1);
262 assert!(!registry.is_empty());
263
264 registry.with_event_state(sig, |state, now| {
266 state.counter.record_suppression(now);
267 });
268
269 assert_eq!(registry.len(), 1);
270 }
271
272 #[test]
273 fn test_clear() {
274 let storage = Arc::new(ShardedStorage::new());
275 let clock = Arc::new(SystemClock::new());
276 let policy = Policy::count_based(100).unwrap();
277 let registry = SuppressionRegistry::new(storage, clock, policy);
278
279 for i in 0..10 {
280 let sig = EventSignature::simple("INFO", &format!("Message {}", i));
281 registry.with_event_state(sig, |_state, _now| {
282 });
284 }
285
286 assert_eq!(registry.len(), 10);
287
288 registry.clear();
289 assert_eq!(registry.len(), 0);
290 assert!(registry.is_empty());
291 }
292
293 #[test]
294 fn test_concurrent_access() {
295 use std::sync::Arc;
296 use std::thread;
297
298 let storage = Arc::new(ShardedStorage::new());
299 let clock = Arc::new(SystemClock::new());
300 let policy = Policy::count_based(100).unwrap();
301 let registry = Arc::new(SuppressionRegistry::new(storage, clock, policy));
302 let mut handles = vec![];
303
304 for i in 0..10 {
305 let registry_clone = Arc::clone(®istry);
306 let handle = thread::spawn(move || {
307 for j in 0..100 {
308 let sig = EventSignature::simple("INFO", &format!("Msg_{}_{}", i, j));
309 registry_clone.with_event_state(sig, |_state, _now| {
310 });
312 }
313 });
314 handles.push(handle);
315 }
316
317 for handle in handles {
318 handle.join().unwrap();
319 }
320
321 assert_eq!(registry.len(), 1000);
322 }
323}