confium_coordinator/coordinator/
metrics_aggregator.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct InstanceMetrics {
8 pub instance_id: String,
9 pub active_sessions: u64,
10 pub total_created: u64,
11 pub total_completed: u64,
12 pub total_expired: u64,
13 pub registered_signers: u64,
14 pub aggregations_attempted: u64,
15 pub aggregations_failed: u64,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct AggregatedMetrics {
21 pub instance_count: usize,
22 pub total_active_sessions: u64,
23 pub total_created: u64,
24 pub total_completed: u64,
25 pub total_expired: u64,
26 pub total_signers: u64,
27 pub total_aggregations: u64,
28 pub total_failures: u64,
29 pub overall_success_rate: f64,
30 pub avg_sessions_per_instance: f64,
31 pub max_active_sessions: u64,
32}
33
34pub fn aggregate(instances: &[InstanceMetrics]) -> AggregatedMetrics {
36 let count = instances.len() as u64;
37 if count == 0 {
38 return AggregatedMetrics {
39 instance_count: 0,
40 total_active_sessions: 0,
41 total_created: 0,
42 total_completed: 0,
43 total_expired: 0,
44 total_signers: 0,
45 total_aggregations: 0,
46 total_failures: 0,
47 overall_success_rate: 1.0,
48 avg_sessions_per_instance: 0.0,
49 max_active_sessions: 0,
50 };
51 }
52
53 let total_active: u64 = instances.iter().map(|i| i.active_sessions).sum();
54 let total_created: u64 = instances.iter().map(|i| i.total_created).sum();
55 let total_completed: u64 = instances.iter().map(|i| i.total_completed).sum();
56 let total_expired: u64 = instances.iter().map(|i| i.total_expired).sum();
57 let total_signers: u64 = instances.iter().map(|i| i.registered_signers).sum();
58 let total_agg: u64 = instances.iter().map(|i| i.aggregations_attempted).sum();
59 let total_fail: u64 = instances.iter().map(|i| i.aggregations_failed).sum();
60 let max_active: u64 = instances
61 .iter()
62 .map(|i| i.active_sessions)
63 .max()
64 .unwrap_or(0);
65 let success_rate = if total_agg > 0 {
66 1.0 - (total_fail as f64 / total_agg as f64)
67 } else {
68 1.0
69 };
70
71 AggregatedMetrics {
72 instance_count: instances.len(),
73 total_active_sessions: total_active,
74 total_created,
75 total_completed,
76 total_expired,
77 total_signers,
78 total_aggregations: total_agg,
79 total_failures: total_fail,
80 overall_success_rate: success_rate,
81 avg_sessions_per_instance: total_active as f64 / count as f64,
82 max_active_sessions: max_active,
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 fn make_instance(id: &str, active: u64, created: u64) -> InstanceMetrics {
91 InstanceMetrics {
92 instance_id: id.into(),
93 active_sessions: active,
94 total_created: created,
95 total_completed: created / 2,
96 total_expired: 0,
97 registered_signers: 3,
98 aggregations_attempted: 100,
99 aggregations_failed: 5,
100 }
101 }
102
103 #[test]
104 fn empty_instances() {
105 let agg = aggregate(&[]);
106 assert_eq!(agg.instance_count, 0);
107 assert_eq!(agg.total_active_sessions, 0);
108 }
109
110 #[test]
111 fn single_instance() {
112 let agg = aggregate(&[make_instance("a", 5, 10)]);
113 assert_eq!(agg.instance_count, 1);
114 assert_eq!(agg.total_active_sessions, 5);
115 assert_eq!(agg.total_created, 10);
116 }
117
118 #[test]
119 fn sums_across_instances() {
120 let agg = aggregate(&[make_instance("a", 5, 10), make_instance("b", 3, 20)]);
121 assert_eq!(agg.total_active_sessions, 8);
122 assert_eq!(agg.total_created, 30);
123 assert_eq!(agg.total_signers, 6);
124 }
125
126 #[test]
127 fn success_rate_computed() {
128 let agg = aggregate(&[make_instance("a", 0, 0)]);
129 assert!((agg.overall_success_rate - 0.95).abs() < 0.001);
130 }
131
132 #[test]
133 fn avg_sessions() {
134 let agg = aggregate(&[make_instance("a", 4, 0), make_instance("b", 6, 0)]);
135 assert!((agg.avg_sessions_per_instance - 5.0).abs() < 0.001);
136 }
137
138 #[test]
139 fn max_active_sessions() {
140 let agg = aggregate(&[
141 make_instance("a", 3, 0),
142 make_instance("b", 7, 0),
143 make_instance("c", 5, 0),
144 ]);
145 assert_eq!(agg.max_active_sessions, 7);
146 }
147
148 #[test]
149 fn serializes() {
150 let agg = aggregate(&[make_instance("a", 1, 1)]);
151 let json = serde_json::to_string(&agg).unwrap();
152 assert!(json.contains("instance_count"));
153 }
154}