metrics_lib/
rate_meter.rs

1//! # Ultra-Fast Rate Meter
2//!
3//! High-performance rate calculations with sliding window tracking.
4//!
5//! ## Features
6//!
7//! - **Sub-microsecond tick operations** - Blazingly fast rate tracking
8//! - **Sliding window calculations** - Accurate rate measurements
9//! - **Multiple time windows** - Second, minute, hour rates
10//! - **Lock-free** - Never blocks, never waits
11//! - **Zero allocations** - Pure atomic operations
12//! - **Rate limiting** - Built-in support for throttling
13
14use crate::{MetricsError, Result};
15use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
16use std::time::{Duration, Instant};
17
18/// Ultra-fast rate meter with sliding window calculations
19///
20/// Tracks events per second/minute/hour with minimal overhead.
21/// Cache-line aligned to prevent false sharing.
22#[repr(align(64))]
23pub struct RateMeter {
24    /// Total events seen (monotonic counter)
25    total_events: AtomicU64,
26    /// Current second's events
27    current_second_events: AtomicU32,
28    /// Current minute's events  
29    current_minute_events: AtomicU32,
30    /// Current hour's events
31    current_hour_events: AtomicU32,
32    /// Last update timestamp (seconds since epoch)
33    last_second: AtomicU64,
34    /// Last minute timestamp
35    last_minute: AtomicU64,
36    /// Last hour timestamp
37    last_hour: AtomicU64,
38    /// Window size for rate calculations (nanoseconds)
39    window_ns: u64,
40    /// Creation timestamp
41    created_at: Instant,
42}
43
44/// Rate statistics
45#[derive(Debug, Clone)]
46pub struct RateStats {
47    /// Total events recorded
48    pub total_events: u64,
49    /// Events per second (current window)
50    pub per_second: f64,
51    /// Events per minute (current window)
52    pub per_minute: f64,
53    /// Events per hour (current window)
54    pub per_hour: f64,
55    /// Average rate since creation
56    pub average_rate: f64,
57    /// Time since creation
58    pub age: Duration,
59    /// Current window fill percentage
60    pub window_fill: f64,
61}
62
63impl RateMeter {
64    /// Create new rate meter with 1-second window
65    #[inline]
66    pub fn new() -> Self {
67        Self::with_window(Duration::from_secs(1))
68    }
69
70    /// Create rate meter with custom window size
71    #[inline]
72    pub fn with_window(window: Duration) -> Self {
73        Self {
74            total_events: AtomicU64::new(0),
75            current_second_events: AtomicU32::new(0),
76            current_minute_events: AtomicU32::new(0),
77            current_hour_events: AtomicU32::new(0),
78            last_second: AtomicU64::new(0),
79            last_minute: AtomicU64::new(0),
80            last_hour: AtomicU64::new(0),
81            window_ns: window.as_nanos() as u64,
82            created_at: Instant::now(),
83        }
84    }
85
86    /// Record an event - THE FASTEST PATH
87    ///
88    /// This is optimized for maximum speed:
89    /// - Single atomic increment for total
90    /// - Lazy window updates only when needed
91    /// - Branch prediction friendly
92    #[inline(always)]
93    pub fn tick(&self) {
94        self.tick_n(1);
95    }
96
97    /// Try to record a single event with overflow checks
98    ///
99    /// Returns `Err(MetricsError::Overflow)` if incrementing the total or any
100    /// current window counter would overflow. On success returns `Ok(())`.
101    ///
102    /// Example
103    /// ```
104    /// use metrics_lib::{RateMeter, MetricsError};
105    /// let m = RateMeter::new();
106    /// m.try_tick().unwrap();
107    /// assert_eq!(m.total(), 1);
108    /// ```
109    #[inline(always)]
110    pub fn try_tick(&self) -> Result<()> {
111        self.try_tick_n(1)
112    }
113
114    /// Record N events at once
115    #[inline(always)]
116    pub fn tick_n(&self, n: u32) {
117        if n == 0 {
118            return;
119        }
120
121        // Always increment total (fastest path)
122        self.total_events.fetch_add(n as u64, Ordering::Relaxed);
123
124        // Update windows (lazy - only when needed)
125        let now = self.get_unix_timestamp();
126        self.update_windows(now, n);
127    }
128
129    /// Try to record N events at once with overflow checks
130    ///
131    /// - Returns `Ok(())` on success.
132    /// - Returns `Err(MetricsError::Overflow)` if incrementing any of the
133    ///   involved counters would overflow (`total_events`, `current_second_events`,
134    ///   `current_minute_events`, or `current_hour_events`).
135    ///
136    /// Example
137    /// ```
138    /// use metrics_lib::{RateMeter, MetricsError};
139    /// let m = RateMeter::new();
140    /// assert!(m.try_tick_n(5).is_ok());
141    /// assert_eq!(m.total(), 5);
142    /// ```
143    #[inline(always)]
144    pub fn try_tick_n(&self, n: u32) -> Result<()> {
145        if n == 0 {
146            return Ok(());
147        }
148
149        // Check total_events overflow
150        let total = self.total_events.load(Ordering::Relaxed);
151        if total.checked_add(n as u64).is_none() {
152            return Err(MetricsError::Overflow);
153        }
154
155        // Pre-check window counters roughly (race-safe best-effort). Since windows reset, we
156        // only guard against immediate and obvious overflow.
157        let sec = self.current_second_events.load(Ordering::Relaxed);
158        if sec.checked_add(n).is_none() {
159            return Err(MetricsError::Overflow);
160        }
161        let min = self.current_minute_events.load(Ordering::Relaxed);
162        if min.checked_add(n).is_none() {
163            return Err(MetricsError::Overflow);
164        }
165        let hour = self.current_hour_events.load(Ordering::Relaxed);
166        if hour.checked_add(n).is_none() {
167            return Err(MetricsError::Overflow);
168        }
169
170        // Apply updates
171        self.total_events.fetch_add(n as u64, Ordering::Relaxed);
172        let now = self.get_unix_timestamp();
173        self.update_windows(now, n);
174        Ok(())
175    }
176
177    /// Get current rate (events per second in current window)
178    #[inline]
179    pub fn rate(&self) -> f64 {
180        let now = self.get_unix_timestamp();
181        self.update_windows(now, 0);
182
183        let events = self.current_second_events.load(Ordering::Relaxed);
184        events as f64
185    }
186
187    /// Get rate per second
188    #[inline]
189    pub fn rate_per_second(&self) -> f64 {
190        self.rate()
191    }
192
193    /// Get rate per minute
194    #[inline]
195    pub fn rate_per_minute(&self) -> f64 {
196        let now = self.get_unix_timestamp();
197        self.update_windows(now, 0);
198
199        let events = self.current_minute_events.load(Ordering::Relaxed);
200        events as f64
201    }
202
203    /// Get rate per hour
204    #[inline]
205    pub fn rate_per_hour(&self) -> f64 {
206        let now = self.get_unix_timestamp();
207        self.update_windows(now, 0);
208
209        let events = self.current_hour_events.load(Ordering::Relaxed);
210        events as f64
211    }
212
213    /// Get total events since creation
214    #[inline(always)]
215    pub fn total(&self) -> u64 {
216        self.total_events.load(Ordering::Relaxed)
217    }
218
219    /// Check if rate exceeds limit
220    #[inline]
221    pub fn exceeds_rate(&self, limit: f64) -> bool {
222        self.rate() > limit
223    }
224
225    /// Check if we can allow N more events without exceeding limit
226    #[inline]
227    pub fn can_allow(&self, n: u32, limit: f64) -> bool {
228        let current_rate = self.rate();
229        (current_rate + n as f64) <= limit
230    }
231
232    /// Rate limiting - tick only if under limit
233    #[inline]
234    pub fn tick_if_under_limit(&self, limit: f64) -> bool {
235        if self.can_allow(1, limit) {
236            self.tick();
237            true
238        } else {
239            false
240        }
241    }
242
243    /// Try to tick if under limit, with overflow checks
244    ///
245    /// Attempts to record a single event only if the current rate would not
246    /// exceed `limit`. Returns:
247    /// - `Ok(true)` if the event was recorded.
248    /// - `Ok(false)` if the event would exceed the limit (no change made).
249    /// - `Err(MetricsError::Overflow)` if counters would overflow while recording.
250    ///
251    /// Example
252    /// ```
253    /// use metrics_lib::RateMeter;
254    /// let m = RateMeter::new();
255    /// assert!(m.try_tick_if_under_limit(10.0).unwrap());
256    /// ```
257    #[inline]
258    pub fn try_tick_if_under_limit(&self, limit: f64) -> Result<bool> {
259        if self.can_allow(1, limit) {
260            self.try_tick()?;
261            Ok(true)
262        } else {
263            Ok(false)
264        }
265    }
266
267    /// Burst rate limiting - allow N events if under limit
268    #[inline]
269    pub fn tick_burst_if_under_limit(&self, n: u32, limit: f64) -> bool {
270        if self.can_allow(n, limit) {
271            self.tick_n(n);
272            true
273        } else {
274            false
275        }
276    }
277
278    /// Reset all counters
279    #[inline]
280    pub fn reset(&self) {
281        let now = self.get_unix_timestamp();
282
283        self.total_events.store(0, Ordering::SeqCst);
284        self.current_second_events.store(0, Ordering::SeqCst);
285        self.current_minute_events.store(0, Ordering::SeqCst);
286        self.current_hour_events.store(0, Ordering::SeqCst);
287        self.last_second.store(now, Ordering::SeqCst);
288        self.last_minute.store(now / 60, Ordering::SeqCst);
289        self.last_hour.store(now / 3600, Ordering::SeqCst);
290    }
291
292    /// Get comprehensive statistics
293    pub fn stats(&self) -> RateStats {
294        let now = self.get_unix_timestamp();
295        self.update_windows(now, 0);
296
297        let total_events = self.total();
298        let per_second = self.current_second_events.load(Ordering::Relaxed) as f64;
299        let per_minute = self.current_minute_events.load(Ordering::Relaxed) as f64;
300        let per_hour = self.current_hour_events.load(Ordering::Relaxed) as f64;
301
302        let age = self.created_at.elapsed();
303        let average_rate = if age.as_secs_f64() > 0.0 {
304            total_events as f64 / age.as_secs_f64()
305        } else {
306            0.0
307        };
308
309        // Calculate window fill (how much of the window has data)
310        let window_fill = if self.window_ns > 0 {
311            let window_seconds = self.window_ns as f64 / 1_000_000_000.0;
312            let elapsed_in_window = age.as_secs_f64().min(window_seconds);
313            (elapsed_in_window / window_seconds * 100.0).min(100.0)
314        } else {
315            100.0
316        };
317
318        RateStats {
319            total_events,
320            per_second,
321            per_minute,
322            per_hour,
323            average_rate,
324            age,
325            window_fill,
326        }
327    }
328
329    /// Get age since creation
330    #[inline]
331    pub fn age(&self) -> Duration {
332        self.created_at.elapsed()
333    }
334
335    /// Check if rate meter is empty (no events recorded)
336    #[inline]
337    pub fn is_empty(&self) -> bool {
338        self.total() == 0
339    }
340
341    // Internal helper methods
342
343    #[inline(always)]
344    fn get_unix_timestamp(&self) -> u64 {
345        self.created_at.elapsed().as_secs()
346            + std::time::SystemTime::now()
347                .duration_since(std::time::UNIX_EPOCH)
348                .unwrap_or_default()
349                .as_secs()
350    }
351
352    #[inline]
353    fn update_windows(&self, now: u64, new_events: u32) {
354        // Update second window
355        let current_second = now;
356        let last_second = self.last_second.load(Ordering::Relaxed);
357
358        if current_second != last_second {
359            // New second - reset counter
360            if self
361                .last_second
362                .compare_exchange(
363                    last_second,
364                    current_second,
365                    Ordering::Relaxed,
366                    Ordering::Relaxed,
367                )
368                .is_ok()
369            {
370                self.current_second_events
371                    .store(new_events, Ordering::Relaxed);
372            } else {
373                // Another thread updated, add to current
374                self.current_second_events
375                    .fetch_add(new_events, Ordering::Relaxed);
376            }
377        } else if new_events > 0 {
378            // Same second - add events
379            self.current_second_events
380                .fetch_add(new_events, Ordering::Relaxed);
381        }
382
383        // Update minute window
384        let current_minute = now / 60;
385        let last_minute = self.last_minute.load(Ordering::Relaxed);
386
387        if current_minute != last_minute {
388            if self
389                .last_minute
390                .compare_exchange(
391                    last_minute,
392                    current_minute,
393                    Ordering::Relaxed,
394                    Ordering::Relaxed,
395                )
396                .is_ok()
397            {
398                self.current_minute_events
399                    .store(new_events, Ordering::Relaxed);
400            } else {
401                self.current_minute_events
402                    .fetch_add(new_events, Ordering::Relaxed);
403            }
404        } else if new_events > 0 {
405            self.current_minute_events
406                .fetch_add(new_events, Ordering::Relaxed);
407        }
408
409        // Update hour window
410        let current_hour = now / 3600;
411        let last_hour = self.last_hour.load(Ordering::Relaxed);
412
413        if current_hour != last_hour {
414            if self
415                .last_hour
416                .compare_exchange(
417                    last_hour,
418                    current_hour,
419                    Ordering::Relaxed,
420                    Ordering::Relaxed,
421                )
422                .is_ok()
423            {
424                self.current_hour_events
425                    .store(new_events, Ordering::Relaxed);
426            } else {
427                self.current_hour_events
428                    .fetch_add(new_events, Ordering::Relaxed);
429            }
430        } else if new_events > 0 {
431            self.current_hour_events
432                .fetch_add(new_events, Ordering::Relaxed);
433        }
434    }
435}
436
437impl Default for RateMeter {
438    #[inline]
439    fn default() -> Self {
440        Self::new()
441    }
442}
443
444impl std::fmt::Display for RateMeter {
445    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
446        write!(f, "RateMeter({:.1}/s, {} total)", self.rate(), self.total())
447    }
448}
449
450impl std::fmt::Debug for RateMeter {
451    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452        let stats = self.stats();
453        f.debug_struct("RateMeter")
454            .field("total_events", &stats.total_events)
455            .field("per_second", &stats.per_second)
456            .field("per_minute", &stats.per_minute)
457            .field("average_rate", &stats.average_rate)
458            .field("age", &stats.age)
459            .finish()
460    }
461}
462
463// Thread safety
464unsafe impl Send for RateMeter {}
465unsafe impl Sync for RateMeter {}
466
467/// Specialized rate meters for common use cases
468pub mod specialized {
469    use super::*;
470
471    /// API rate limiter (requests per second)
472    #[repr(align(64))]
473    pub struct ApiRateLimiter {
474        meter: RateMeter,
475        limit: AtomicU32, // requests per second
476    }
477
478    impl ApiRateLimiter {
479        /// Create API rate limiter with requests per second limit
480        #[inline]
481        pub fn new(requests_per_second: u32) -> Self {
482            Self {
483                meter: RateMeter::new(),
484                limit: AtomicU32::new(requests_per_second),
485            }
486        }
487
488        /// Try to make a request - returns true if allowed
489        #[inline]
490        pub fn try_request(&self) -> bool {
491            let limit = self.limit.load(Ordering::Relaxed) as f64;
492            self.meter.tick_if_under_limit(limit)
493        }
494
495        /// Try to make N requests - returns true if all allowed
496        #[inline]
497        pub fn try_requests(&self, n: u32) -> bool {
498            let limit = self.limit.load(Ordering::Relaxed) as f64;
499            self.meter.tick_burst_if_under_limit(n, limit)
500        }
501
502        /// Update the rate limit
503        #[inline]
504        pub fn set_limit(&self, requests_per_second: u32) {
505            self.limit.store(requests_per_second, Ordering::Relaxed);
506        }
507
508        /// Get current limit
509        #[inline]
510        pub fn get_limit(&self) -> u32 {
511            self.limit.load(Ordering::Relaxed)
512        }
513
514        /// Get current rate
515        #[inline]
516        pub fn current_rate(&self) -> f64 {
517            self.meter.rate()
518        }
519
520        /// Get total requests
521        #[inline]
522        pub fn total_requests(&self) -> u64 {
523            self.meter.total()
524        }
525
526        /// Check if currently over limit
527        #[inline]
528        pub fn is_over_limit(&self) -> bool {
529            let limit = self.limit.load(Ordering::Relaxed) as f64;
530            self.meter.rate() > limit
531        }
532
533        /// Reset the rate limiter
534        #[inline]
535        pub fn reset(&self) {
536            self.meter.reset();
537        }
538    }
539
540    impl Default for ApiRateLimiter {
541        fn default() -> Self {
542            Self::new(1000)
543        } // 1000 req/s default
544    }
545
546    /// Throughput meter for measuring data rates
547    #[repr(align(64))]
548    pub struct ThroughputMeter {
549        meter: RateMeter,
550    }
551
552    impl ThroughputMeter {
553        /// Create new throughput meter
554        #[inline]
555        pub fn new() -> Self {
556            Self {
557                meter: RateMeter::new(),
558            }
559        }
560
561        /// Record bytes transferred
562        #[inline(always)]
563        pub fn record_bytes(&self, bytes: u64) {
564            self.meter.tick_n(bytes as u32);
565        }
566
567        /// Get bytes per second
568        #[inline]
569        pub fn bytes_per_second(&self) -> f64 {
570            self.meter.rate()
571        }
572
573        /// Get kilobytes per second
574        #[inline]
575        pub fn kb_per_second(&self) -> f64 {
576            self.meter.rate() / 1024.0
577        }
578
579        /// Get megabytes per second
580        #[inline]
581        pub fn mb_per_second(&self) -> f64 {
582            self.meter.rate() / (1024.0 * 1024.0)
583        }
584
585        /// Get gigabytes per second
586        #[inline]
587        pub fn gb_per_second(&self) -> f64 {
588            self.meter.rate() / (1024.0 * 1024.0 * 1024.0)
589        }
590
591        /// Get total bytes transferred
592        #[inline]
593        pub fn total_bytes(&self) -> u64 {
594            self.meter.total()
595        }
596
597        /// Reset the throughput meter
598        #[inline]
599        pub fn reset(&self) {
600            self.meter.reset();
601        }
602    }
603
604    impl Default for ThroughputMeter {
605        fn default() -> Self {
606            Self::new()
607        }
608    }
609}
610
611#[cfg(test)]
612mod tests {
613    use super::*;
614    use std::sync::Arc;
615    use std::thread;
616
617    #[test]
618    fn test_basic_operations() {
619        let meter = RateMeter::new();
620
621        assert!(meter.is_empty());
622        assert_eq!(meter.total(), 0);
623        assert_eq!(meter.rate(), 0.0);
624
625        meter.tick();
626        assert!(!meter.is_empty());
627        assert_eq!(meter.total(), 1);
628
629        meter.tick_n(5);
630        assert_eq!(meter.total(), 6);
631    }
632
633    #[test]
634    fn test_rate_calculations() {
635        let meter = RateMeter::new();
636
637        // Record events rapidly in same second
638        for _ in 0..100 {
639            meter.tick();
640        }
641
642        let rate = meter.rate();
643        assert_eq!(rate, 100.0);
644        assert_eq!(meter.rate_per_second(), 100.0);
645    }
646
647    #[test]
648    fn test_multiple_windows() {
649        let meter = RateMeter::new();
650
651        // Record events
652        for _ in 0..60 {
653            meter.tick();
654        }
655
656        let stats = meter.stats();
657        assert_eq!(stats.total_events, 60);
658        assert_eq!(stats.per_second, 60.0);
659        assert_eq!(stats.per_minute, 60.0);
660        assert_eq!(stats.per_hour, 60.0);
661    }
662
663    #[test]
664    fn test_rate_limiting() {
665        let meter = RateMeter::new();
666
667        // Allow requests under limit
668        assert!(meter.tick_if_under_limit(10.0));
669        assert!(meter.tick_if_under_limit(10.0));
670
671        // Add more to approach limit
672        meter.tick_n(8);
673
674        // Should now be at/over limit
675        assert!(!meter.tick_if_under_limit(10.0));
676        assert!(meter.exceeds_rate(9.0));
677        assert!(!meter.exceeds_rate(11.0));
678    }
679
680    #[test]
681    fn test_burst_rate_limiting() {
682        let meter = RateMeter::new();
683
684        // Try burst under limit
685        assert!(meter.tick_burst_if_under_limit(5, 10.0));
686        assert_eq!(meter.total(), 5);
687
688        // Try burst that would exceed limit
689        assert!(!meter.tick_burst_if_under_limit(10, 10.0));
690        assert_eq!(meter.total(), 5); // Should be unchanged
691
692        // Try smaller burst that fits
693        assert!(meter.tick_burst_if_under_limit(3, 10.0));
694        assert_eq!(meter.total(), 8);
695    }
696
697    #[test]
698    fn test_can_allow() {
699        let meter = RateMeter::new();
700
701        meter.tick_n(5);
702
703        assert!(meter.can_allow(3, 10.0)); // 5 + 3 = 8 <= 10
704        assert!(!meter.can_allow(6, 10.0)); // 5 + 6 = 11 > 10
705        assert!(meter.can_allow(5, 10.0)); // 5 + 5 = 10 <= 10
706    }
707
708    #[test]
709    fn test_reset() {
710        let meter = RateMeter::new();
711
712        meter.tick_n(100);
713        assert_eq!(meter.total(), 100);
714        assert!(meter.rate() > 0.0);
715
716        meter.reset();
717        assert_eq!(meter.total(), 0);
718        assert_eq!(meter.rate(), 0.0);
719        assert!(meter.is_empty());
720    }
721
722    #[test]
723    fn test_statistics() {
724        let meter = RateMeter::new();
725
726        meter.tick_n(50);
727
728        let stats = meter.stats();
729        assert_eq!(stats.total_events, 50);
730        assert_eq!(stats.per_second, 50.0);
731        assert!(stats.average_rate > 0.0);
732        assert!(stats.age > Duration::from_nanos(0));
733        assert!(stats.window_fill >= 0.0);
734    }
735
736    #[test]
737    fn test_api_rate_limiter() {
738        let limiter = specialized::ApiRateLimiter::new(10);
739
740        // Should allow requests under limit
741        for _ in 0..10 {
742            assert!(limiter.try_request());
743        }
744
745        // Should deny request over limit
746        assert!(!limiter.try_request());
747
748        // Check status - rate is at limit (10), not over it since request was denied
749        assert_eq!(limiter.current_rate(), 10.0);
750        assert_eq!(limiter.total_requests(), 10);
751        assert_eq!(limiter.get_limit(), 10);
752
753        // Update limit
754        limiter.set_limit(20);
755        assert_eq!(limiter.get_limit(), 20);
756        assert!(!limiter.is_over_limit()); // Now under new limit
757
758        // Test burst requests
759        limiter.reset();
760        assert!(limiter.try_requests(5));
761        assert_eq!(limiter.total_requests(), 5);
762
763        assert!(!limiter.try_requests(20)); // Would exceed limit
764        assert_eq!(limiter.total_requests(), 5); // Unchanged
765    }
766
767    #[test]
768    fn test_throughput_meter() {
769        let meter = specialized::ThroughputMeter::new();
770
771        meter.record_bytes(1024); // 1 KB
772        assert_eq!(meter.bytes_per_second(), 1024.0);
773        assert_eq!(meter.kb_per_second(), 1.0);
774        assert_eq!(meter.total_bytes(), 1024);
775
776        meter.record_bytes(1024 * 1024); // 1 MB more
777        assert_eq!(meter.total_bytes(), 1024 + 1024 * 1024);
778        assert!((meter.mb_per_second() - 1.001).abs() < 0.01);
779    }
780
781    #[test]
782    fn test_high_concurrency() {
783        let meter = Arc::new(RateMeter::new());
784        let num_threads = 50;
785        let ticks_per_thread = 1000;
786
787        let handles: Vec<_> = (0..num_threads)
788            .map(|_| {
789                let meter = Arc::clone(&meter);
790                thread::spawn(move || {
791                    for _ in 0..ticks_per_thread {
792                        meter.tick();
793                    }
794                })
795            })
796            .collect();
797
798        for handle in handles {
799            handle.join().unwrap();
800        }
801
802        assert_eq!(meter.total(), num_threads * ticks_per_thread);
803
804        let stats = meter.stats();
805        assert!(stats.average_rate > 0.0);
806        assert_eq!(stats.total_events, num_threads * ticks_per_thread);
807    }
808
809    #[test]
810    fn test_concurrent_rate_limiting() {
811        let limiter = Arc::new(specialized::ApiRateLimiter::new(100));
812        let num_threads = 20;
813
814        let handles: Vec<_> = (0..num_threads)
815            .map(|_| {
816                let limiter = Arc::clone(&limiter);
817                thread::spawn(move || {
818                    let mut successful = 0;
819                    for _ in 0..10 {
820                        if limiter.try_request() {
821                            successful += 1;
822                        }
823                    }
824                    successful
825                })
826            })
827            .collect();
828
829        let total_successful: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
830
831        // Should be limited to around 100 requests
832        // Allow some tolerance for concurrent overshoot on slower/contended runners
833        // When running under coverage instrumentation, timing can skew more; relax upper bound.
834        let upper_bound = if cfg!(coverage) { 160 } else { 120 };
835        assert!(
836            total_successful <= upper_bound,
837            "total_successful={} > upper_bound={}",
838            total_successful,
839            upper_bound,
840        );
841        assert!(
842            total_successful >= 90,
843            "total_successful={} < lower_bound=90",
844            total_successful,
845        ); // Account for timing variations
846    }
847
848    #[test]
849    fn test_display_and_debug() {
850        let meter = RateMeter::new();
851        meter.tick_n(42);
852
853        let display_str = format!("{meter}");
854        assert!(display_str.contains("RateMeter"));
855        assert!(display_str.contains("42 total"));
856
857        let debug_str = format!("{meter:?}");
858        assert!(debug_str.contains("RateMeter"));
859        assert!(debug_str.contains("total_events"));
860    }
861
862    #[test]
863    fn test_custom_window() {
864        let meter = RateMeter::with_window(Duration::from_secs(5));
865
866        meter.tick_n(10);
867        assert_eq!(meter.total(), 10);
868        assert_eq!(meter.rate(), 10.0);
869
870        let stats = meter.stats();
871        assert!(stats.window_fill >= 0.0);
872    }
873
874    // New tests for try_ variants and overflow/error conditions
875    #[test]
876    fn test_try_tick_and_try_tick_n_ok() {
877        let meter = RateMeter::new();
878        assert!(meter.try_tick().is_ok());
879        assert!(meter.try_tick_n(5).is_ok());
880        assert_eq!(meter.total(), 6);
881    }
882
883    #[test]
884    fn test_try_tick_n_total_overflow() {
885        let meter = RateMeter::new();
886        // Directly set near overflow
887        meter.total_events.store(u64::MAX - 1, Ordering::Relaxed);
888        // Adding 2 should overflow
889        let err = meter.try_tick_n(2).unwrap_err();
890        assert_eq!(err, MetricsError::Overflow);
891    }
892
893    #[test]
894    fn test_try_tick_n_window_overflow() {
895        let meter = RateMeter::new();
896        // Force current windows to near overflow and ensure we are in the same second/minute/hour
897        let now = meter.get_unix_timestamp();
898        meter.last_second.store(now, Ordering::Relaxed);
899        meter.last_minute.store(now / 60, Ordering::Relaxed);
900        meter.last_hour.store(now / 3600, Ordering::Relaxed);
901
902        meter
903            .current_second_events
904            .store(u32::MAX - 1, Ordering::Relaxed);
905        meter
906            .current_minute_events
907            .store(u32::MAX - 1, Ordering::Relaxed);
908        meter
909            .current_hour_events
910            .store(u32::MAX - 1, Ordering::Relaxed);
911
912        // Any n >= 2 should trip the pre-check overflow guard
913        let err = meter.try_tick_n(2).unwrap_err();
914        assert_eq!(err, MetricsError::Overflow);
915    }
916
917    #[test]
918    fn test_try_tick_if_under_limit() {
919        let meter = RateMeter::new();
920        // Under limit should tick and return true
921        assert!(meter.try_tick_if_under_limit(10.0).unwrap());
922        // Drive rate up close to limit
923        assert!(meter.try_tick_n(8).is_ok());
924        // Now at 9; allow one more at limit 10
925        assert!(meter.try_tick_if_under_limit(10.0).unwrap());
926        // Next should be denied (would exceed)
927        assert!(!meter.try_tick_if_under_limit(10.0).unwrap());
928    }
929}
930
931#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
932#[allow(unused_imports)]
933mod benchmarks {
934    use super::*;
935    use std::time::Instant;
936
937    #[cfg_attr(not(feature = "bench-tests"), ignore)]
938    #[test]
939    fn bench_rate_meter_tick() {
940        let meter = RateMeter::new();
941        let iterations = 10_000_000;
942
943        let start = Instant::now();
944        for _ in 0..iterations {
945            meter.tick();
946        }
947        let elapsed = start.elapsed();
948
949        println!(
950            "RateMeter tick: {:.2} ns/op",
951            elapsed.as_nanos() as f64 / iterations as f64
952        );
953
954        assert_eq!(meter.total(), iterations);
955        // Should be under 400ns per tick (relaxed from 200ns)
956        assert!(elapsed.as_nanos() / (iterations as u128) < 400);
957    }
958
959    #[cfg_attr(not(feature = "bench-tests"), ignore)]
960    #[test]
961    fn bench_rate_meter_tick_n() {
962        let meter = RateMeter::new();
963        let iterations = 1_000_000;
964
965        let start = Instant::now();
966        for i in 0..iterations {
967            meter.tick_n((i % 10) + 1);
968        }
969        let elapsed = start.elapsed();
970
971        println!(
972            "RateMeter tick_n: {:.2} ns/op",
973            elapsed.as_nanos() as f64 / iterations as f64
974        );
975
976        // Should be under 500ns per tick_n (relaxed from 300ns)
977        assert!(elapsed.as_nanos() / (iterations as u128) < 500);
978    }
979
980    #[cfg_attr(not(feature = "bench-tests"), ignore)]
981    #[test]
982    fn bench_rate_calculation() {
983        let meter = RateMeter::new();
984
985        // Fill with data
986        meter.tick_n(1000);
987
988        let iterations = 1_000_000;
989        let start = Instant::now();
990
991        for _ in 0..iterations {
992            let _ = meter.rate();
993        }
994
995        let elapsed = start.elapsed();
996        println!(
997            "RateMeter rate: {:.2} ns/op",
998            elapsed.as_nanos() as f64 / iterations as f64
999        );
1000
1001        // Should be very fast (relaxed from 100ns to 300ns)
1002        assert!(elapsed.as_nanos() / iterations < 300);
1003    }
1004
1005    #[cfg_attr(not(feature = "bench-tests"), ignore)]
1006    #[test]
1007    fn bench_api_rate_limiter() {
1008        let limiter = specialized::ApiRateLimiter::new(1_000_000); // High limit
1009        let iterations = 1_000_000;
1010
1011        let start = Instant::now();
1012        for _ in 0..iterations {
1013            let _ = limiter.try_request();
1014        }
1015        let elapsed = start.elapsed();
1016
1017        println!(
1018            "ApiRateLimiter try_request: {:.2} ns/op",
1019            elapsed.as_nanos() as f64 / iterations as f64
1020        );
1021
1022        // Should be under 1000ns per request (relaxed from 300ns)
1023        assert!(elapsed.as_nanos() / iterations < 1000);
1024    }
1025}