metrics_lib/
registry.rs

1//! Lock-free registry for storing and retrieving metrics by name.
2//!
3//! The Registry uses a lock-free hash table with memory-efficient string interning
4//! to provide O(1) metric lookup with minimal memory overhead.
5
6use crate::{Counter, Gauge, RateMeter, Timer};
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9
10/// A thread-safe registry for storing metrics by name.
11///
12/// Uses RwLock for simplicity while maintaining good performance for the
13/// read-heavy workloads typical in metrics collection.
14#[repr(align(64))]
15pub struct Registry {
16    counters: RwLock<HashMap<String, Arc<Counter>>>,
17    gauges: RwLock<HashMap<String, Arc<Gauge>>>,
18    timers: RwLock<HashMap<String, Arc<Timer>>>,
19    rate_meters: RwLock<HashMap<String, Arc<RateMeter>>>,
20}
21
22impl Registry {
23    /// Create a new empty registry.
24    pub fn new() -> Self {
25        Self {
26            counters: RwLock::new(HashMap::new()),
27            gauges: RwLock::new(HashMap::new()),
28            timers: RwLock::new(HashMap::new()),
29            rate_meters: RwLock::new(HashMap::new()),
30        }
31    }
32
33    /// Get or create a counter by name.
34    pub fn get_or_create_counter(&self, name: &str) -> Arc<Counter> {
35        // Fast path: try read lock first
36        if let Ok(counters) = self.counters.read() {
37            if let Some(counter) = counters.get(name) {
38                return counter.clone();
39            }
40        }
41
42        // Slow path: write lock to create new counter
43        let mut counters = self.counters.write().unwrap_or_else(|e| e.into_inner());
44        counters
45            .entry(name.to_string())
46            .or_insert_with(|| Arc::new(Counter::new()))
47            .clone()
48    }
49
50    /// Get or create a gauge by name.
51    pub fn get_or_create_gauge(&self, name: &str) -> Arc<Gauge> {
52        // Fast path: try read lock first
53        if let Ok(gauges) = self.gauges.read() {
54            if let Some(gauge) = gauges.get(name) {
55                return gauge.clone();
56            }
57        }
58
59        // Slow path: write lock to create new gauge
60        let mut gauges = self.gauges.write().unwrap_or_else(|e| e.into_inner());
61        gauges
62            .entry(name.to_string())
63            .or_insert_with(|| Arc::new(Gauge::new()))
64            .clone()
65    }
66
67    /// Get or create a timer by name.
68    pub fn get_or_create_timer(&self, name: &str) -> Arc<Timer> {
69        // Fast path: try read lock first
70        if let Ok(timers) = self.timers.read() {
71            if let Some(timer) = timers.get(name) {
72                return timer.clone();
73            }
74        }
75
76        // Slow path: write lock to create new timer
77        let mut timers = self.timers.write().unwrap_or_else(|e| e.into_inner());
78        timers
79            .entry(name.to_string())
80            .or_insert_with(|| Arc::new(Timer::new()))
81            .clone()
82    }
83
84    /// Get or create a rate meter by name.
85    pub fn get_or_create_rate_meter(&self, name: &str) -> Arc<RateMeter> {
86        // Fast path: try read lock first
87        if let Ok(rate_meters) = self.rate_meters.read() {
88            if let Some(rate_meter) = rate_meters.get(name) {
89                return rate_meter.clone();
90            }
91        }
92
93        // Slow path: write lock to create new rate meter
94        let mut rate_meters = self.rate_meters.write().unwrap_or_else(|e| e.into_inner());
95        rate_meters
96            .entry(name.to_string())
97            .or_insert_with(|| Arc::new(RateMeter::new()))
98            .clone()
99    }
100
101    /// Get all registered counter names.
102    pub fn counter_names(&self) -> Vec<String> {
103        self.counters
104            .read()
105            .unwrap_or_else(|e| e.into_inner())
106            .keys()
107            .cloned()
108            .collect()
109    }
110
111    /// Get all registered gauge names.
112    pub fn gauge_names(&self) -> Vec<String> {
113        self.gauges
114            .read()
115            .unwrap_or_else(|e| e.into_inner())
116            .keys()
117            .cloned()
118            .collect()
119    }
120
121    /// Get all registered timer names.
122    pub fn timer_names(&self) -> Vec<String> {
123        self.timers
124            .read()
125            .unwrap_or_else(|e| e.into_inner())
126            .keys()
127            .cloned()
128            .collect()
129    }
130
131    /// Get all registered rate meter names.
132    pub fn rate_meter_names(&self) -> Vec<String> {
133        self.rate_meters
134            .read()
135            .unwrap_or_else(|e| e.into_inner())
136            .keys()
137            .cloned()
138            .collect()
139    }
140
141    /// Get total number of registered metrics.
142    pub fn metric_count(&self) -> usize {
143        self.counters
144            .read()
145            .unwrap_or_else(|e| e.into_inner())
146            .len()
147            + self.gauges.read().unwrap_or_else(|e| e.into_inner()).len()
148            + self.timers.read().unwrap_or_else(|e| e.into_inner()).len()
149            + self
150                .rate_meters
151                .read()
152                .unwrap_or_else(|e| e.into_inner())
153                .len()
154    }
155
156    /// Clear all metrics from the registry.
157    pub fn clear(&self) {
158        self.counters
159            .write()
160            .unwrap_or_else(|e| e.into_inner())
161            .clear();
162        self.gauges
163            .write()
164            .unwrap_or_else(|e| e.into_inner())
165            .clear();
166        self.timers
167            .write()
168            .unwrap_or_else(|e| e.into_inner())
169            .clear();
170        self.rate_meters
171            .write()
172            .unwrap_or_else(|e| e.into_inner())
173            .clear();
174    }
175}
176
177impl Default for Registry {
178    fn default() -> Self {
179        Self::new()
180    }
181}
182
183// `Registry` is Send + Sync automatically because all fields are `RwLock<...>`
184// whose contents are Send + Sync. No unsafe impls required.
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use std::sync::Arc;
190    use std::thread;
191
192    #[test]
193    fn test_counter_registration() {
194        let registry = Registry::new();
195
196        let counter1 = registry.get_or_create_counter("requests");
197        let counter2 = registry.get_or_create_counter("requests");
198
199        // Should return the same instance
200        assert!(Arc::ptr_eq(&counter1, &counter2));
201        assert_eq!(registry.metric_count(), 1);
202    }
203
204    #[test]
205    fn test_gauge_registration() {
206        let registry = Registry::new();
207
208        let gauge1 = registry.get_or_create_gauge("cpu_usage");
209        let gauge2 = registry.get_or_create_gauge("cpu_usage");
210
211        // Should return the same instance
212        assert!(Arc::ptr_eq(&gauge1, &gauge2));
213        assert_eq!(registry.metric_count(), 1);
214    }
215
216    #[test]
217    fn test_timer_registration() {
218        let registry = Registry::new();
219
220        let timer1 = registry.get_or_create_timer("db_query");
221        let timer2 = registry.get_or_create_timer("db_query");
222
223        // Should return the same instance
224        assert!(Arc::ptr_eq(&timer1, &timer2));
225        assert_eq!(registry.metric_count(), 1);
226    }
227
228    #[test]
229    fn test_rate_meter_registration() {
230        let registry = Registry::new();
231
232        let meter1 = registry.get_or_create_rate_meter("api_calls");
233        let meter2 = registry.get_or_create_rate_meter("api_calls");
234
235        // Should return the same instance
236        assert!(Arc::ptr_eq(&meter1, &meter2));
237        assert_eq!(registry.metric_count(), 1);
238    }
239
240    #[test]
241    fn test_mixed_metrics() {
242        let registry = Registry::new();
243
244        let _counter = registry.get_or_create_counter("requests");
245        let _gauge = registry.get_or_create_gauge("cpu_usage");
246        let _timer = registry.get_or_create_timer("db_query");
247        let _meter = registry.get_or_create_rate_meter("api_calls");
248
249        assert_eq!(registry.metric_count(), 4);
250        assert_eq!(registry.counter_names().len(), 1);
251        assert_eq!(registry.gauge_names().len(), 1);
252        assert_eq!(registry.timer_names().len(), 1);
253        assert_eq!(registry.rate_meter_names().len(), 1);
254    }
255
256    #[test]
257    fn test_concurrent_access() {
258        let registry = Arc::new(Registry::new());
259        let mut handles = vec![];
260
261        // Spawn multiple threads accessing the same counter
262        for _i in 0..10 {
263            let registry = registry.clone();
264            let handle = thread::spawn(move || {
265                let counter = registry.get_or_create_counter("concurrent_test");
266                counter.inc();
267                counter.get()
268            });
269            handles.push(handle);
270        }
271
272        // Wait for all threads to complete
273        for handle in handles {
274            handle.join().unwrap();
275        }
276
277        // Should have exactly one counter registered
278        assert_eq!(registry.metric_count(), 1);
279        let counter = registry.get_or_create_counter("concurrent_test");
280        assert_eq!(counter.get(), 10);
281    }
282
283    #[test]
284    fn test_clear() {
285        let registry = Registry::new();
286
287        let _counter = registry.get_or_create_counter("requests");
288        let _gauge = registry.get_or_create_gauge("cpu_usage");
289
290        assert_eq!(registry.metric_count(), 2);
291
292        registry.clear();
293        assert_eq!(registry.metric_count(), 0);
294    }
295
296    #[test]
297    fn test_metric_names() {
298        let registry = Registry::new();
299
300        let _counter1 = registry.get_or_create_counter("requests");
301        let _counter2 = registry.get_or_create_counter("errors");
302        let _gauge1 = registry.get_or_create_gauge("cpu_usage");
303
304        let counter_names = registry.counter_names();
305        let gauge_names = registry.gauge_names();
306
307        assert_eq!(counter_names.len(), 2);
308        assert_eq!(gauge_names.len(), 1);
309        assert!(counter_names.contains(&"requests".to_string()));
310        assert!(counter_names.contains(&"errors".to_string()));
311        assert!(gauge_names.contains(&"cpu_usage".to_string()));
312    }
313
314    #[test]
315    fn test_duplicate_names_across_types_are_independent() {
316        let registry = Registry::new();
317
318        let c = registry.get_or_create_counter("same_name");
319        let g = registry.get_or_create_gauge("same_name");
320        let t = registry.get_or_create_timer("same_name");
321        let r = registry.get_or_create_rate_meter("same_name");
322
323        // All should be registered independently (4 metrics total)
324        assert_eq!(registry.metric_count(), 4);
325
326        // And they should be distinct types; at least ensure their addresses differ pairwise
327        let c_addr = Arc::as_ptr(&c) as usize;
328        let g_addr = Arc::as_ptr(&g) as usize;
329        let t_addr = Arc::as_ptr(&t) as usize;
330        let r_addr = Arc::as_ptr(&r) as usize;
331
332        assert_ne!(c_addr, g_addr);
333        assert_ne!(c_addr, t_addr);
334        assert_ne!(c_addr, r_addr);
335        assert_ne!(g_addr, t_addr);
336        assert_ne!(g_addr, r_addr);
337        assert_ne!(t_addr, r_addr);
338    }
339
340    #[test]
341    fn test_clear_then_recreate_returns_new_instances() {
342        let registry = Registry::new();
343
344        let counter_before = registry.get_or_create_counter("requests");
345        let gauge_before = registry.get_or_create_gauge("cpu");
346        assert_eq!(registry.metric_count(), 2);
347
348        // Clear the registry; previously returned Arcs still exist but registry is empty
349        registry.clear();
350        assert_eq!(registry.metric_count(), 0);
351
352        let counter_after = registry.get_or_create_counter("requests");
353        let gauge_after = registry.get_or_create_gauge("cpu");
354
355        // New instances should NOT be ptr-equal to the old ones
356        assert!(!Arc::ptr_eq(&counter_before, &counter_after));
357        assert!(!Arc::ptr_eq(&gauge_before, &gauge_after));
358    }
359
360    #[test]
361    fn test_concurrent_duplicate_registration_singleton_per_name() {
362        let registry = Arc::new(Registry::new());
363        let mut handles = vec![];
364
365        for _ in 0..16 {
366            let r = registry.clone();
367            handles.push(thread::spawn(move || r.get_or_create_timer("dup")));
368        }
369
370        let first = registry.get_or_create_timer("dup");
371        for h in handles {
372            let timer = h.join().unwrap();
373            assert!(Arc::ptr_eq(&first, &timer));
374        }
375        assert_eq!(registry.metric_count(), 1);
376    }
377}