1#[derive(Debug, Clone)]
9pub struct CacheStats {
10 hits: u64,
12 misses: u64,
14 evictions: u64,
16 insertions: u64,
18 bytes_cached: u64,
20 capacity_bytes: u64,
22}
23
24impl Default for CacheStats {
25 fn default() -> Self {
26 Self::new(0)
27 }
28}
29
30impl CacheStats {
31 #[must_use]
33 pub fn new(capacity_bytes: u64) -> Self {
34 Self {
35 hits: 0,
36 misses: 0,
37 evictions: 0,
38 insertions: 0,
39 bytes_cached: 0,
40 capacity_bytes,
41 }
42 }
43
44 #[must_use]
46 pub fn for_l1_cache() -> Self {
47 Self::new(32 * 1024)
48 }
49
50 #[must_use]
52 pub fn for_l2_cache() -> Self {
53 Self::new(256 * 1024)
54 }
55
56 #[must_use]
58 pub fn for_app_cache() -> Self {
59 Self::new(16 * 1024 * 1024)
60 }
61
62 pub fn hit(&mut self) {
64 self.hits += 1;
65 }
66
67 pub fn miss(&mut self) {
69 self.misses += 1;
70 }
71
72 pub fn evict(&mut self, bytes: u64) {
74 self.evictions += 1;
75 self.bytes_cached = self.bytes_cached.saturating_sub(bytes);
76 }
77
78 pub fn insert(&mut self, bytes: u64) {
80 self.insertions += 1;
81 self.bytes_cached += bytes;
82 }
83
84 #[must_use]
86 pub fn hit_rate(&self) -> f64 {
87 let total = self.hits + self.misses;
88 if total == 0 {
89 0.0
90 } else {
91 (self.hits as f64 / total as f64) * 100.0
92 }
93 }
94
95 #[must_use]
97 pub fn miss_rate(&self) -> f64 {
98 100.0 - self.hit_rate()
99 }
100
101 #[must_use]
103 pub fn eviction_rate(&self) -> f64 {
104 if self.insertions == 0 {
105 0.0
106 } else {
107 self.evictions as f64 / self.insertions as f64
108 }
109 }
110
111 #[must_use]
113 pub fn fill_percentage(&self) -> f64 {
114 if self.capacity_bytes == 0 {
115 0.0
116 } else {
117 (self.bytes_cached as f64 / self.capacity_bytes as f64) * 100.0
118 }
119 }
120
121 #[must_use]
123 pub fn total_requests(&self) -> u64 {
124 self.hits + self.misses
125 }
126
127 #[must_use]
129 pub fn is_effective(&self, threshold: f64) -> bool {
130 self.hit_rate() >= threshold
131 }
132
133 pub fn reset(&mut self) {
135 self.hits = 0;
136 self.misses = 0;
137 self.evictions = 0;
138 self.insertions = 0;
139 self.bytes_cached = 0;
140 }
141}
142
143#[derive(Debug, Clone)]
148pub struct BloomFilter {
149 bits: [u64; 16], hash_count: u32,
153 items: u64,
155}
156
157impl Default for BloomFilter {
158 fn default() -> Self {
159 Self::new(3)
160 }
161}
162
163impl BloomFilter {
164 #[must_use]
166 pub fn new(hash_count: u32) -> Self {
167 Self {
168 bits: [0; 16],
169 hash_count: hash_count.clamp(1, 10),
170 items: 0,
171 }
172 }
173
174 #[must_use]
176 pub fn for_small() -> Self {
177 Self::new(3)
178 }
179
180 #[must_use]
182 pub fn for_medium() -> Self {
183 Self::new(5)
184 }
185
186 fn hash(&self, value: u64, seed: u32) -> usize {
188 let mut h = value.wrapping_mul(0x517cc1b727220a95);
189 h = h.wrapping_add(seed as u64);
190 h ^= h >> 33;
191 h = h.wrapping_mul(0xff51afd7ed558ccd);
192 (h as usize) % 1024
193 }
194
195 pub fn add(&mut self, value: u64) {
197 for i in 0..self.hash_count {
198 let bit_idx = self.hash(value, i);
199 let word_idx = bit_idx / 64;
200 let bit_pos = bit_idx % 64;
201 self.bits[word_idx] |= 1 << bit_pos;
202 }
203 self.items += 1;
204 }
205
206 #[must_use]
208 pub fn might_contain(&self, value: u64) -> bool {
209 for i in 0..self.hash_count {
210 let bit_idx = self.hash(value, i);
211 let word_idx = bit_idx / 64;
212 let bit_pos = bit_idx % 64;
213 if self.bits[word_idx] & (1 << bit_pos) == 0 {
214 return false;
215 }
216 }
217 true
218 }
219
220 #[must_use]
222 pub fn len(&self) -> u64 {
223 self.items
224 }
225
226 #[must_use]
228 pub fn is_empty(&self) -> bool {
229 self.items == 0
230 }
231
232 #[must_use]
234 pub fn false_positive_rate(&self) -> f64 {
235 let m = 1024.0; let k = self.hash_count as f64;
237 let n = self.items as f64;
238 if n == 0.0 {
239 return 0.0;
240 }
241 (1.0 - (-k * n / m).exp()).powf(k)
242 }
243
244 #[must_use]
246 pub fn fill_percentage(&self) -> f64 {
247 let set_bits: u32 = self.bits.iter().map(|w| w.count_ones()).sum();
248 (set_bits as f64 / 1024.0) * 100.0
249 }
250
251 pub fn reset(&mut self) {
253 self.bits = [0; 16];
254 self.items = 0;
255 }
256}
257
258#[derive(Debug, Clone)]
262pub struct LoadBalancer {
263 weights: [u32; 8],
265 current: [i32; 8],
267 active: usize,
269 dispatched: u64,
271 per_backend: [u64; 8],
273}
274
275impl Default for LoadBalancer {
276 fn default() -> Self {
277 Self::new()
278 }
279}
280
281impl LoadBalancer {
282 #[must_use]
284 pub fn new() -> Self {
285 Self {
286 weights: [0; 8],
287 current: [0; 8],
288 active: 0,
289 dispatched: 0,
290 per_backend: [0; 8],
291 }
292 }
293
294 #[must_use]
296 pub fn equal_weights(n: usize) -> Self {
297 let mut lb = Self::new();
298 for _ in 0..n.min(8) {
299 lb.add_backend(1);
300 }
301 lb
302 }
303
304 pub fn add_backend(&mut self, weight: u32) {
306 if self.active < 8 {
307 self.weights[self.active] = weight.max(1);
308 self.current[self.active] = 0;
309 self.active += 1;
310 }
311 }
312
313 #[must_use]
315 pub fn select_backend(&mut self) -> Option<usize> {
316 if self.active == 0 {
317 return None;
318 }
319
320 let total_weight: i32 = self.weights[..self.active].iter().map(|&w| w as i32).sum();
322
323 for i in 0..self.active {
325 self.current[i] += self.weights[i] as i32;
326 }
327
328 let mut max_idx = 0;
330 let mut max_weight = self.current[0];
331 for i in 1..self.active {
332 if self.current[i] > max_weight {
333 max_weight = self.current[i];
334 max_idx = i;
335 }
336 }
337
338 self.current[max_idx] -= total_weight;
340 self.dispatched += 1;
341 self.per_backend[max_idx] += 1;
342
343 Some(max_idx)
344 }
345
346 #[must_use]
348 pub fn distribution(&self, backend: usize) -> f64 {
349 if self.dispatched == 0 || backend >= self.active {
350 0.0
351 } else {
352 (self.per_backend[backend] as f64 / self.dispatched as f64) * 100.0
353 }
354 }
355
356 #[must_use]
358 pub fn total_dispatched(&self) -> u64 {
359 self.dispatched
360 }
361
362 #[must_use]
364 pub fn backend_count(&self) -> usize {
365 self.active
366 }
367
368 #[must_use]
370 pub fn is_balanced(&self, threshold: f64) -> bool {
371 if self.active <= 1 || self.dispatched < 10 {
372 return true;
373 }
374 let avg = self.dispatched as f64 / self.active as f64;
375 for i in 0..self.active {
376 let deviation = ((self.per_backend[i] as f64 - avg) / avg).abs() * 100.0;
377 if deviation > threshold {
378 return false;
379 }
380 }
381 true
382 }
383
384 pub fn reset(&mut self) {
386 self.current = [0; 8];
387 self.dispatched = 0;
388 self.per_backend = [0; 8];
389 }
390}
391
392#[derive(Debug, Clone)]
396pub struct BurstTracker {
397 tokens: f64,
399 capacity: f64,
401 refill_rate: f64,
403 last_update_us: u64,
405 burst_count: u64,
407 max_burst: u64,
409 total_bursts: u64,
411}
412
413impl Default for BurstTracker {
414 fn default() -> Self {
415 Self::new(100.0, 10.0)
416 }
417}
418
419impl BurstTracker {
420 #[must_use]
422 pub fn new(capacity: f64, refill_rate: f64) -> Self {
423 Self {
424 tokens: capacity,
425 capacity: capacity.max(1.0),
426 refill_rate: refill_rate.max(0.1),
427 last_update_us: 0,
428 burst_count: 0,
429 max_burst: 0,
430 total_bursts: 0,
431 }
432 }
433
434 #[must_use]
436 pub fn for_api() -> Self {
437 Self::new(100.0, 50.0)
438 }
439
440 #[must_use]
442 pub fn for_network() -> Self {
443 Self::new(1000.0, 100.0)
444 }
445
446 pub fn consume(&mut self, tokens: f64, now_us: u64) -> bool {
448 self.refill(now_us);
449
450 if tokens <= self.tokens {
451 self.tokens -= tokens;
452 self.burst_count += 1;
453 if self.burst_count > self.max_burst {
454 self.max_burst = self.burst_count;
455 }
456 true
457 } else {
458 if self.burst_count > 0 {
460 self.total_bursts += 1;
461 }
462 self.burst_count = 0;
463 false
464 }
465 }
466
467 fn refill(&mut self, now_us: u64) {
468 if self.last_update_us == 0 {
469 self.last_update_us = now_us;
470 return;
471 }
472 let elapsed_s = (now_us.saturating_sub(self.last_update_us)) as f64 / 1_000_000.0;
473 let refill = elapsed_s * self.refill_rate;
474 self.tokens = (self.tokens + refill).min(self.capacity);
475 self.last_update_us = now_us;
476 }
477
478 #[must_use]
480 pub fn tokens(&self) -> f64 {
481 self.tokens
482 }
483
484 #[must_use]
486 pub fn fill_percentage(&self) -> f64 {
487 (self.tokens / self.capacity) * 100.0
488 }
489
490 #[must_use]
492 pub fn max_burst(&self) -> u64 {
493 self.max_burst
494 }
495
496 #[must_use]
498 pub fn total_bursts(&self) -> u64 {
499 self.total_bursts
500 }
501
502 #[must_use]
504 pub fn avg_burst(&self) -> f64 {
505 if self.total_bursts == 0 {
506 0.0
507 } else {
508 self.max_burst as f64 }
510 }
511
512 pub fn reset(&mut self) {
514 self.tokens = self.capacity;
515 self.burst_count = 0;
516 self.max_burst = 0;
517 self.total_bursts = 0;
518 self.last_update_us = 0;
519 }
520}
521
522#[derive(Debug, Clone)]
529pub struct TopKTracker {
530 values: [f64; 32],
531 count: usize,
532 k: usize,
533}
534
535impl Default for TopKTracker {
536 fn default() -> Self {
537 Self::new(10)
538 }
539}
540
541impl TopKTracker {
542 #[must_use]
544 pub fn new(k: usize) -> Self {
545 Self {
546 values: [f64::NEG_INFINITY; 32],
547 count: 0,
548 k: k.min(32),
549 }
550 }
551
552 #[must_use]
554 pub fn for_metrics() -> Self {
555 Self::new(10)
556 }
557
558 #[must_use]
560 pub fn for_processes() -> Self {
561 Self::new(20)
562 }
563
564 pub fn add(&mut self, value: f64) {
566 if self.count < self.k {
567 let mut i = self.count;
569 while i > 0 && self.values[i - 1] < value {
570 self.values[i] = self.values[i - 1];
571 i -= 1;
572 }
573 self.values[i] = value;
574 self.count += 1;
575 } else if value > self.values[self.k - 1] {
576 let mut i = self.k - 1;
578 while i > 0 && self.values[i - 1] < value {
579 self.values[i] = self.values[i - 1];
580 i -= 1;
581 }
582 self.values[i] = value;
583 }
584 }
585
586 #[must_use]
588 pub fn top(&self) -> &[f64] {
589 &self.values[..self.count]
590 }
591
592 #[must_use]
594 pub fn k(&self) -> usize {
595 self.k
596 }
597
598 #[must_use]
600 pub fn count(&self) -> usize {
601 self.count
602 }
603
604 #[must_use]
606 pub fn minimum(&self) -> Option<f64> {
607 if self.count > 0 {
608 Some(self.values[self.count - 1])
609 } else {
610 None
611 }
612 }
613
614 #[must_use]
616 pub fn maximum(&self) -> Option<f64> {
617 if self.count > 0 {
618 Some(self.values[0])
619 } else {
620 None
621 }
622 }
623
624 pub fn reset(&mut self) {
626 self.values = [f64::NEG_INFINITY; 32];
627 self.count = 0;
628 }
629}
630
631#[derive(Debug, Clone)]
638pub struct QuotaTracker {
639 limit: u64,
640 used: u64,
641 peak_usage: u64,
642}
643
644impl Default for QuotaTracker {
645 fn default() -> Self {
646 Self::new(1000)
647 }
648}
649
650impl QuotaTracker {
651 #[must_use]
653 pub fn new(limit: u64) -> Self {
654 Self {
655 limit: limit.max(1),
656 used: 0,
657 peak_usage: 0,
658 }
659 }
660
661 #[must_use]
663 pub fn for_api_daily() -> Self {
664 Self::new(10000)
665 }
666
667 #[must_use]
669 pub fn for_storage_gb() -> Self {
670 Self::new(100)
671 }
672
673 pub fn use_quota(&mut self, amount: u64) -> bool {
675 if self.used + amount > self.limit {
676 false
677 } else {
678 self.used += amount;
679 if self.used > self.peak_usage {
680 self.peak_usage = self.used;
681 }
682 true
683 }
684 }
685
686 pub fn release(&mut self, amount: u64) {
688 self.used = self.used.saturating_sub(amount);
689 }
690
691 #[must_use]
693 pub fn limit(&self) -> u64 {
694 self.limit
695 }
696
697 #[must_use]
699 pub fn remaining(&self) -> u64 {
700 self.limit.saturating_sub(self.used)
701 }
702
703 #[must_use]
705 pub fn usage_percentage(&self) -> f64 {
706 (self.used as f64 / self.limit as f64) * 100.0
707 }
708
709 #[must_use]
711 pub fn is_exhausted(&self) -> bool {
712 self.used >= self.limit
713 }
714
715 #[must_use]
717 pub fn peak_usage(&self) -> u64 {
718 self.peak_usage
719 }
720
721 pub fn reset(&mut self) {
723 self.used = 0;
724 self.peak_usage = 0;
725 }
726}
727
728#[derive(Debug, Clone)]
735pub struct FrequencyCounter {
736 counts: [u64; 16],
737 total: u64,
738}
739
740impl Default for FrequencyCounter {
741 fn default() -> Self {
742 Self::new()
743 }
744}
745
746impl FrequencyCounter {
747 #[must_use]
749 pub fn new() -> Self {
750 Self {
751 counts: [0; 16],
752 total: 0,
753 }
754 }
755
756 pub fn increment(&mut self, category: usize) {
758 if category < 16 {
759 self.counts[category] += 1;
760 self.total += 1;
761 }
762 }
763
764 pub fn add(&mut self, category: usize, count: u64) {
766 if category < 16 {
767 self.counts[category] += count;
768 self.total += count;
769 }
770 }
771
772 #[must_use]
774 pub fn count(&self, category: usize) -> u64 {
775 if category < 16 {
776 self.counts[category]
777 } else {
778 0
779 }
780 }
781
782 #[must_use]
784 pub fn frequency(&self, category: usize) -> f64 {
785 if self.total == 0 || category >= 16 {
786 0.0
787 } else {
788 (self.counts[category] as f64 / self.total as f64) * 100.0
789 }
790 }
791
792 #[must_use]
794 pub fn total(&self) -> u64 {
795 self.total
796 }
797
798 #[must_use]
800 pub fn most_frequent(&self) -> Option<usize> {
801 if self.total == 0 {
802 return None;
803 }
804 let mut max_idx = 0;
805 let mut max_count = self.counts[0];
806 for i in 1..16 {
807 if self.counts[i] > max_count {
808 max_count = self.counts[i];
809 max_idx = i;
810 }
811 }
812 Some(max_idx)
813 }
814
815 #[must_use]
817 pub fn non_zero_count(&self) -> usize {
818 self.counts.iter().filter(|&&c| c > 0).count()
819 }
820
821 #[must_use]
823 pub fn entropy(&self) -> f64 {
824 if self.total == 0 {
825 return 0.0;
826 }
827 let mut entropy = 0.0;
828 for &count in &self.counts {
829 if count > 0 {
830 let p = count as f64 / self.total as f64;
831 entropy -= p * p.log2();
832 }
833 }
834 entropy / 4.0
836 }
837
838 pub fn reset(&mut self) {
840 self.counts = [0; 16];
841 self.total = 0;
842 }
843}
844
845#[derive(Debug, Clone)]
852pub struct MovingRange {
853 values: [f64; 128],
854 window_size: usize,
855 head: usize,
856 count: usize,
857 current_min: f64,
858 current_max: f64,
859}
860
861impl Default for MovingRange {
862 fn default() -> Self {
863 Self::new(10)
864 }
865}
866
867impl MovingRange {
868 #[must_use]
870 pub fn new(window_size: usize) -> Self {
871 Self {
872 values: [0.0; 128],
873 window_size: window_size.min(128),
874 head: 0,
875 count: 0,
876 current_min: f64::INFINITY,
877 current_max: f64::NEG_INFINITY,
878 }
879 }
880
881 #[must_use]
883 pub fn for_prices() -> Self {
884 Self::new(20)
885 }
886
887 #[must_use]
889 pub fn for_latency() -> Self {
890 Self::new(100)
891 }
892
893 pub fn add(&mut self, value: f64) {
895 let idx = self.head;
896 self.values[idx] = value;
897 self.head = (self.head + 1) % self.window_size;
898 if self.count < self.window_size {
899 self.count += 1;
900 }
901 self.recalculate_minmax();
902 }
903
904 fn recalculate_minmax(&mut self) {
905 self.current_min = f64::INFINITY;
906 self.current_max = f64::NEG_INFINITY;
907 for i in 0..self.count {
908 let v = self.values[i];
909 if v < self.current_min {
910 self.current_min = v;
911 }
912 if v > self.current_max {
913 self.current_max = v;
914 }
915 }
916 }
917
918 #[must_use]
920 pub fn window_size(&self) -> usize {
921 self.window_size
922 }
923
924 #[must_use]
926 pub fn count(&self) -> usize {
927 self.count
928 }
929
930 #[must_use]
932 pub fn min(&self) -> Option<f64> {
933 if self.count > 0 {
934 Some(self.current_min)
935 } else {
936 None
937 }
938 }
939
940 #[must_use]
942 pub fn max(&self) -> Option<f64> {
943 if self.count > 0 {
944 Some(self.current_max)
945 } else {
946 None
947 }
948 }
949
950 #[must_use]
952 pub fn range(&self) -> f64 {
953 if self.count > 0 {
954 self.current_max - self.current_min
955 } else {
956 0.0
957 }
958 }
959
960 #[must_use]
962 pub fn midrange(&self) -> f64 {
963 if self.count > 0 {
964 (self.current_max + self.current_min) / 2.0
965 } else {
966 0.0
967 }
968 }
969
970 #[must_use]
972 pub fn volatility(&self) -> f64 {
973 let mid = self.midrange();
974 if mid.abs() < 0.0001 {
975 0.0
976 } else {
977 (self.range() / mid) * 100.0
978 }
979 }
980
981 pub fn reset(&mut self) {
983 self.values = [0.0; 128];
984 self.head = 0;
985 self.count = 0;
986 self.current_min = f64::INFINITY;
987 self.current_max = f64::NEG_INFINITY;
988 }
989}
990
991#[derive(Debug, Clone)]
998pub struct TimeoutTracker {
999 timeout_us: u64,
1000 total: u64,
1001 timed_out: u64,
1002 last_duration_us: u64,
1003 max_duration_us: u64,
1004}
1005
1006impl Default for TimeoutTracker {
1007 fn default() -> Self {
1008 Self::new(1_000_000) }
1010}
1011
1012impl TimeoutTracker {
1013 #[must_use]
1015 pub fn new(timeout_us: u64) -> Self {
1016 Self {
1017 timeout_us: timeout_us.max(1),
1018 total: 0,
1019 timed_out: 0,
1020 last_duration_us: 0,
1021 max_duration_us: 0,
1022 }
1023 }
1024
1025 #[must_use]
1027 pub fn for_network() -> Self {
1028 Self::new(5_000_000)
1029 }
1030
1031 #[must_use]
1033 pub fn for_database() -> Self {
1034 Self::new(30_000_000)
1035 }
1036
1037 #[must_use]
1039 pub fn for_fast() -> Self {
1040 Self::new(100_000)
1041 }
1042
1043 pub fn record(&mut self, duration_us: u64) {
1045 self.total += 1;
1046 self.last_duration_us = duration_us;
1047 if duration_us > self.max_duration_us {
1048 self.max_duration_us = duration_us;
1049 }
1050 if duration_us > self.timeout_us {
1051 self.timed_out += 1;
1052 }
1053 }
1054
1055 #[must_use]
1057 pub fn total(&self) -> u64 {
1058 self.total
1059 }
1060
1061 #[must_use]
1063 pub fn timed_out(&self) -> u64 {
1064 self.timed_out
1065 }
1066
1067 #[must_use]
1069 pub fn timeout_rate(&self) -> f64 {
1070 if self.total == 0 {
1071 0.0
1072 } else {
1073 (self.timed_out as f64 / self.total as f64) * 100.0
1074 }
1075 }
1076
1077 #[must_use]
1079 pub fn success_rate(&self) -> f64 {
1080 100.0 - self.timeout_rate()
1081 }
1082
1083 #[must_use]
1085 pub fn is_healthy(&self, max_timeout_rate: f64) -> bool {
1086 self.timeout_rate() <= max_timeout_rate
1087 }
1088
1089 #[must_use]
1091 pub fn max_duration_us(&self) -> u64 {
1092 self.max_duration_us
1093 }
1094
1095 #[must_use]
1097 pub fn timeout_threshold_us(&self) -> u64 {
1098 self.timeout_us
1099 }
1100
1101 pub fn reset(&mut self) {
1103 self.total = 0;
1104 self.timed_out = 0;
1105 self.last_duration_us = 0;
1106 self.max_duration_us = 0;
1107 }
1108}
1109
1110#[derive(Debug, Clone)]
1117pub struct RetryTracker {
1118 max_retries: u32,
1119 base_delay_ms: u64,
1120 max_delay_ms: u64,
1121 total_attempts: u64,
1122 total_retries: u64,
1123 successful_retries: u64,
1124 current_retry: u32,
1125}
1126
1127impl Default for RetryTracker {
1128 fn default() -> Self {
1129 Self::new(3, 100, 10000)
1130 }
1131}
1132
1133impl RetryTracker {
1134 #[must_use]
1136 pub fn new(max_retries: u32, base_delay_ms: u64, max_delay_ms: u64) -> Self {
1137 Self {
1138 max_retries,
1139 base_delay_ms: base_delay_ms.max(1),
1140 max_delay_ms: max_delay_ms.max(base_delay_ms),
1141 total_attempts: 0,
1142 total_retries: 0,
1143 successful_retries: 0,
1144 current_retry: 0,
1145 }
1146 }
1147
1148 #[must_use]
1150 pub fn for_api() -> Self {
1151 Self::new(3, 100, 10000)
1152 }
1153
1154 #[must_use]
1156 pub fn for_network() -> Self {
1157 Self::new(5, 1000, 30000)
1158 }
1159
1160 pub fn attempt(&mut self) {
1162 self.total_attempts += 1;
1163 }
1164
1165 pub fn retry(&mut self) {
1167 self.total_retries += 1;
1168 if self.current_retry < self.max_retries {
1169 self.current_retry += 1;
1170 }
1171 }
1172
1173 pub fn success(&mut self) {
1175 if self.current_retry > 0 {
1176 self.successful_retries += 1;
1177 }
1178 self.current_retry = 0;
1179 }
1180
1181 #[must_use]
1183 pub fn next_delay_ms(&self) -> u64 {
1184 let delay = self.base_delay_ms * (1 << self.current_retry);
1185 delay.min(self.max_delay_ms)
1186 }
1187
1188 #[must_use]
1190 pub fn retries_exhausted(&self) -> bool {
1191 self.current_retry >= self.max_retries
1192 }
1193
1194 #[must_use]
1196 pub fn retry_rate(&self) -> f64 {
1197 if self.total_attempts == 0 {
1198 0.0
1199 } else {
1200 (self.total_retries as f64 / self.total_attempts as f64) * 100.0
1201 }
1202 }
1203
1204 #[must_use]
1206 pub fn successful_retry_rate(&self) -> f64 {
1207 if self.total_retries == 0 {
1208 0.0
1209 } else {
1210 (self.successful_retries as f64 / self.total_retries as f64) * 100.0
1211 }
1212 }
1213
1214 #[must_use]
1216 pub fn current_retry(&self) -> u32 {
1217 self.current_retry
1218 }
1219
1220 pub fn reset(&mut self) {
1222 self.total_attempts = 0;
1223 self.total_retries = 0;
1224 self.successful_retries = 0;
1225 self.current_retry = 0;
1226 }
1227}
1228
1229#[derive(Debug, Clone)]
1236pub struct ScheduleSlot {
1237 slot_duration_us: u64,
1238 num_slots: usize,
1239 current_slot: usize,
1240 slot_start_us: u64,
1241 executions_per_slot: [u64; 16],
1242}
1243
1244impl Default for ScheduleSlot {
1245 fn default() -> Self {
1246 Self::new(1_000_000, 10) }
1248}
1249
1250impl ScheduleSlot {
1251 #[must_use]
1253 pub fn new(slot_duration_us: u64, num_slots: usize) -> Self {
1254 Self {
1255 slot_duration_us: slot_duration_us.max(1),
1256 num_slots: num_slots.min(16).max(1),
1257 current_slot: 0,
1258 slot_start_us: 0,
1259 executions_per_slot: [0; 16],
1260 }
1261 }
1262
1263 #[must_use]
1265 pub fn for_round_robin() -> Self {
1266 Self::new(1_000_000, 10)
1267 }
1268
1269 #[must_use]
1271 pub fn for_minute() -> Self {
1272 Self::new(60_000_000, 5)
1273 }
1274
1275 pub fn update(&mut self, now_us: u64) {
1277 if self.slot_start_us == 0 {
1278 self.slot_start_us = now_us;
1279 return;
1280 }
1281
1282 let elapsed = now_us.saturating_sub(self.slot_start_us);
1283 let slots_passed = (elapsed / self.slot_duration_us) as usize;
1284
1285 if slots_passed > 0 {
1286 self.current_slot = (self.current_slot + slots_passed) % self.num_slots;
1287 self.slot_start_us = now_us;
1288 }
1289 }
1290
1291 pub fn execute(&mut self, now_us: u64) {
1293 self.update(now_us);
1294 if self.current_slot < 16 {
1295 self.executions_per_slot[self.current_slot] += 1;
1296 }
1297 }
1298
1299 #[must_use]
1301 pub fn current_slot(&self) -> usize {
1302 self.current_slot
1303 }
1304
1305 #[must_use]
1307 pub fn num_slots(&self) -> usize {
1308 self.num_slots
1309 }
1310
1311 #[must_use]
1313 pub fn executions(&self, slot: usize) -> u64 {
1314 if slot < 16 {
1315 self.executions_per_slot[slot]
1316 } else {
1317 0
1318 }
1319 }
1320
1321 #[must_use]
1323 pub fn total_executions(&self) -> u64 {
1324 self.executions_per_slot[..self.num_slots].iter().sum()
1325 }
1326
1327 #[must_use]
1329 pub fn is_balanced(&self, threshold: f64) -> bool {
1330 let total = self.total_executions();
1331 if total == 0 {
1332 return true;
1333 }
1334 let expected = total as f64 / self.num_slots as f64;
1335 for i in 0..self.num_slots {
1336 let diff = (self.executions_per_slot[i] as f64 - expected).abs();
1337 if diff / expected * 100.0 > threshold {
1338 return false;
1339 }
1340 }
1341 true
1342 }
1343
1344 pub fn reset(&mut self) {
1346 self.current_slot = 0;
1347 self.slot_start_us = 0;
1348 self.executions_per_slot = [0; 16];
1349 }
1350}
1351
1352#[derive(Debug, Clone)]
1359pub struct CooldownTimer {
1360 cooldown_us: u64,
1361 last_action_us: u64,
1362 total_actions: u64,
1363 blocked_attempts: u64,
1364}
1365
1366impl Default for CooldownTimer {
1367 fn default() -> Self {
1368 Self::new(1_000_000) }
1370}
1371
1372impl CooldownTimer {
1373 #[must_use]
1375 pub fn new(cooldown_us: u64) -> Self {
1376 Self {
1377 cooldown_us: cooldown_us.max(1),
1378 last_action_us: 0,
1379 total_actions: 0,
1380 blocked_attempts: 0,
1381 }
1382 }
1383
1384 #[must_use]
1386 pub fn for_fast() -> Self {
1387 Self::new(100_000)
1388 }
1389
1390 #[must_use]
1392 pub fn for_normal() -> Self {
1393 Self::new(1_000_000)
1394 }
1395
1396 #[must_use]
1398 pub fn for_slow() -> Self {
1399 Self::new(10_000_000)
1400 }
1401
1402 #[must_use]
1404 pub fn is_ready(&self, now_us: u64) -> bool {
1405 if self.last_action_us == 0 {
1406 return true;
1407 }
1408 now_us.saturating_sub(self.last_action_us) >= self.cooldown_us
1409 }
1410
1411 pub fn try_action(&mut self, now_us: u64) -> bool {
1413 if self.is_ready(now_us) {
1414 self.last_action_us = now_us;
1415 self.total_actions += 1;
1416 true
1417 } else {
1418 self.blocked_attempts += 1;
1419 false
1420 }
1421 }
1422
1423 pub fn force_action(&mut self, now_us: u64) {
1425 self.last_action_us = now_us;
1426 self.total_actions += 1;
1427 }
1428
1429 #[must_use]
1431 pub fn remaining_us(&self, now_us: u64) -> u64 {
1432 if self.is_ready(now_us) {
1433 0
1434 } else {
1435 self.cooldown_us
1436 .saturating_sub(now_us.saturating_sub(self.last_action_us))
1437 }
1438 }
1439
1440 #[must_use]
1442 pub fn cooldown_us(&self) -> u64 {
1443 self.cooldown_us
1444 }
1445
1446 #[must_use]
1448 pub fn total_actions(&self) -> u64 {
1449 self.total_actions
1450 }
1451
1452 #[must_use]
1454 pub fn blocked_attempts(&self) -> u64 {
1455 self.blocked_attempts
1456 }
1457
1458 #[must_use]
1460 pub fn block_rate(&self) -> f64 {
1461 let total = self.total_actions + self.blocked_attempts;
1462 if total == 0 {
1463 0.0
1464 } else {
1465 (self.blocked_attempts as f64 / total as f64) * 100.0
1466 }
1467 }
1468
1469 pub fn reset(&mut self) {
1471 self.last_action_us = 0;
1472 self.total_actions = 0;
1473 self.blocked_attempts = 0;
1474 }
1475}
1476
1477#[derive(Debug, Clone)]
1484pub struct BackpressureMonitor {
1485 signals: u64,
1486 total_ops: u64,
1487 consecutive: u32,
1488 max_consecutive: u32,
1489 last_signal_us: u64,
1490}
1491
1492impl Default for BackpressureMonitor {
1493 fn default() -> Self {
1494 Self::new()
1495 }
1496}
1497
1498impl BackpressureMonitor {
1499 #[must_use]
1501 pub fn new() -> Self {
1502 Self {
1503 signals: 0,
1504 total_ops: 0,
1505 consecutive: 0,
1506 max_consecutive: 0,
1507 last_signal_us: 0,
1508 }
1509 }
1510
1511 pub fn success(&mut self) {
1513 self.total_ops += 1;
1514 self.consecutive = 0;
1515 }
1516
1517 pub fn signal(&mut self, now_us: u64) {
1519 self.signals += 1;
1520 self.total_ops += 1;
1521 self.consecutive += 1;
1522 self.last_signal_us = now_us;
1523 if self.consecutive > self.max_consecutive {
1524 self.max_consecutive = self.consecutive;
1525 }
1526 }
1527
1528 #[must_use]
1530 pub fn pressure_rate(&self) -> f64 {
1531 if self.total_ops == 0 {
1532 0.0
1533 } else {
1534 (self.signals as f64 / self.total_ops as f64) * 100.0
1535 }
1536 }
1537
1538 #[must_use]
1540 pub fn is_under_pressure(&self, threshold: u32) -> bool {
1541 self.consecutive >= threshold
1542 }
1543
1544 #[must_use]
1546 pub fn consecutive(&self) -> u32 {
1547 self.consecutive
1548 }
1549
1550 #[must_use]
1552 pub fn max_consecutive(&self) -> u32 {
1553 self.max_consecutive
1554 }
1555
1556 #[must_use]
1558 pub fn total_signals(&self) -> u64 {
1559 self.signals
1560 }
1561
1562 #[must_use]
1564 pub fn is_healthy(&self, max_rate: f64) -> bool {
1565 self.pressure_rate() <= max_rate
1566 }
1567
1568 pub fn reset(&mut self) {
1570 self.signals = 0;
1571 self.total_ops = 0;
1572 self.consecutive = 0;
1573 self.max_consecutive = 0;
1574 self.last_signal_us = 0;
1575 }
1576}
1577
1578#[derive(Debug, Clone)]
1585pub struct CapacityPlanner {
1586 capacity: u64,
1587 current: u64,
1588 peak: u64,
1589 samples: u32,
1590 sum_utilization: f64,
1591 growth_rate: f64,
1592}
1593
1594impl Default for CapacityPlanner {
1595 fn default() -> Self {
1596 Self::new(1000)
1597 }
1598}
1599
1600impl CapacityPlanner {
1601 #[must_use]
1603 pub fn new(capacity: u64) -> Self {
1604 Self {
1605 capacity: capacity.max(1),
1606 current: 0,
1607 peak: 0,
1608 samples: 0,
1609 sum_utilization: 0.0,
1610 growth_rate: 0.0,
1611 }
1612 }
1613
1614 #[must_use]
1616 pub fn for_connections() -> Self {
1617 Self::new(1000)
1618 }
1619
1620 #[must_use]
1622 pub fn for_storage() -> Self {
1623 Self::new(100)
1624 }
1625
1626 pub fn update(&mut self, current: u64) {
1628 let old = self.current;
1629 self.current = current;
1630 if current > self.peak {
1631 self.peak = current;
1632 }
1633 self.samples += 1;
1634 self.sum_utilization += self.utilization();
1635
1636 if old > 0 {
1638 self.growth_rate = (current as f64 - old as f64) / old as f64;
1639 }
1640 }
1641
1642 #[must_use]
1644 pub fn utilization(&self) -> f64 {
1645 (self.current as f64 / self.capacity as f64) * 100.0
1646 }
1647
1648 #[must_use]
1650 pub fn peak_utilization(&self) -> f64 {
1651 (self.peak as f64 / self.capacity as f64) * 100.0
1652 }
1653
1654 #[must_use]
1656 pub fn avg_utilization(&self) -> f64 {
1657 if self.samples == 0 {
1658 0.0
1659 } else {
1660 self.sum_utilization / self.samples as f64
1661 }
1662 }
1663
1664 #[must_use]
1666 pub fn remaining(&self) -> u64 {
1667 self.capacity.saturating_sub(self.current)
1668 }
1669
1670 #[must_use]
1672 pub fn at_risk(&self, threshold: f64) -> bool {
1673 self.utilization() >= threshold
1674 }
1675
1676 #[must_use]
1678 pub fn growth_rate(&self) -> f64 {
1679 self.growth_rate
1680 }
1681
1682 pub fn reset(&mut self) {
1684 self.current = 0;
1685 self.peak = 0;
1686 self.samples = 0;
1687 self.sum_utilization = 0.0;
1688 self.growth_rate = 0.0;
1689 }
1690}
1691
1692#[derive(Debug, Clone)]
1699pub struct DriftTracker {
1700 expected_interval_us: u64,
1701 last_timestamp_us: u64,
1702 total_drift_us: i64,
1703 samples: u64,
1704 max_drift_us: i64,
1705 min_drift_us: i64,
1706}
1707
1708impl Default for DriftTracker {
1709 fn default() -> Self {
1710 Self::new(1_000_000) }
1712}
1713
1714impl DriftTracker {
1715 #[must_use]
1717 pub fn new(expected_interval_us: u64) -> Self {
1718 Self {
1719 expected_interval_us: expected_interval_us.max(1),
1720 last_timestamp_us: 0,
1721 total_drift_us: 0,
1722 samples: 0,
1723 max_drift_us: i64::MIN,
1724 min_drift_us: i64::MAX,
1725 }
1726 }
1727
1728 #[must_use]
1730 pub fn for_60fps() -> Self {
1731 Self::new(16_667)
1732 }
1733
1734 #[must_use]
1736 pub fn for_heartbeat() -> Self {
1737 Self::new(1_000_000)
1738 }
1739
1740 pub fn record(&mut self, now_us: u64) {
1742 if self.last_timestamp_us == 0 {
1743 self.last_timestamp_us = now_us;
1744 return;
1745 }
1746
1747 let actual_interval = now_us.saturating_sub(self.last_timestamp_us);
1748 let drift = actual_interval as i64 - self.expected_interval_us as i64;
1749
1750 self.total_drift_us += drift;
1751 self.samples += 1;
1752
1753 if drift > self.max_drift_us {
1754 self.max_drift_us = drift;
1755 }
1756 if drift < self.min_drift_us {
1757 self.min_drift_us = drift;
1758 }
1759
1760 self.last_timestamp_us = now_us;
1761 }
1762
1763 #[must_use]
1765 pub fn avg_drift_us(&self) -> f64 {
1766 if self.samples == 0 {
1767 0.0
1768 } else {
1769 self.total_drift_us as f64 / self.samples as f64
1770 }
1771 }
1772
1773 #[must_use]
1775 pub fn max_drift_us(&self) -> i64 {
1776 if self.samples == 0 {
1777 0
1778 } else {
1779 self.max_drift_us
1780 }
1781 }
1782
1783 #[must_use]
1785 pub fn min_drift_us(&self) -> i64 {
1786 if self.samples == 0 {
1787 0
1788 } else {
1789 self.min_drift_us
1790 }
1791 }
1792
1793 #[must_use]
1795 pub fn is_stable(&self, tolerance_us: i64) -> bool {
1796 self.avg_drift_us().abs() < tolerance_us as f64
1797 }
1798
1799 #[must_use]
1801 pub fn drift_range_us(&self) -> i64 {
1802 if self.samples == 0 {
1803 0
1804 } else {
1805 self.max_drift_us - self.min_drift_us
1806 }
1807 }
1808
1809 #[must_use]
1811 pub fn samples(&self) -> u64 {
1812 self.samples
1813 }
1814
1815 pub fn reset(&mut self) {
1817 self.last_timestamp_us = 0;
1818 self.total_drift_us = 0;
1819 self.samples = 0;
1820 self.max_drift_us = i64::MIN;
1821 self.min_drift_us = i64::MAX;
1822 }
1823}
1824
1825#[derive(Debug, Clone)]
1832pub struct SemaphoreTracker {
1833 total_permits: u32,
1834 acquired: u32,
1835 peak_acquired: u32,
1836 acquisitions: u64,
1837 releases: u64,
1838 contentions: u64,
1839}
1840
1841impl Default for SemaphoreTracker {
1842 fn default() -> Self {
1843 Self::new(10)
1844 }
1845}
1846
1847impl SemaphoreTracker {
1848 #[must_use]
1850 pub fn new(total_permits: u32) -> Self {
1851 Self {
1852 total_permits: total_permits.max(1),
1853 acquired: 0,
1854 peak_acquired: 0,
1855 acquisitions: 0,
1856 releases: 0,
1857 contentions: 0,
1858 }
1859 }
1860
1861 #[must_use]
1863 pub fn for_database() -> Self {
1864 Self::new(20)
1865 }
1866
1867 #[must_use]
1869 pub fn for_workers() -> Self {
1870 Self::new(8)
1871 }
1872
1873 pub fn try_acquire(&mut self) -> bool {
1875 if self.acquired < self.total_permits {
1876 self.acquired += 1;
1877 self.acquisitions += 1;
1878 if self.acquired > self.peak_acquired {
1879 self.peak_acquired = self.acquired;
1880 }
1881 true
1882 } else {
1883 self.contentions += 1;
1884 false
1885 }
1886 }
1887
1888 pub fn release(&mut self) {
1890 if self.acquired > 0 {
1891 self.acquired -= 1;
1892 self.releases += 1;
1893 }
1894 }
1895
1896 #[must_use]
1898 pub fn available(&self) -> u32 {
1899 self.total_permits.saturating_sub(self.acquired)
1900 }
1901
1902 #[must_use]
1904 pub fn utilization(&self) -> f64 {
1905 (self.acquired as f64 / self.total_permits as f64) * 100.0
1906 }
1907
1908 #[must_use]
1910 pub fn peak_utilization(&self) -> f64 {
1911 (self.peak_acquired as f64 / self.total_permits as f64) * 100.0
1912 }
1913
1914 #[must_use]
1916 pub fn contention_rate(&self) -> f64 {
1917 let total = self.acquisitions + self.contentions;
1918 if total == 0 {
1919 0.0
1920 } else {
1921 (self.contentions as f64 / total as f64) * 100.0
1922 }
1923 }
1924
1925 #[must_use]
1927 pub fn is_healthy(&self, max_contention: f64) -> bool {
1928 self.contention_rate() <= max_contention
1929 }
1930
1931 #[must_use]
1933 pub fn total_permits(&self) -> u32 {
1934 self.total_permits
1935 }
1936
1937 pub fn reset(&mut self) {
1939 self.acquired = 0;
1940 self.peak_acquired = 0;
1941 self.acquisitions = 0;
1942 self.releases = 0;
1943 self.contentions = 0;
1944 }
1945}