Skip to main content

agent_uri_dht/
stats.rs

1//! Statistics and result types for DHT evaluation.
2
3use std::time::Duration;
4
5use crate::Endpoint;
6
7/// Statistics about the DHT state.
8///
9/// Used for evaluation metrics and monitoring.
10#[derive(Debug, Clone, Default)]
11pub struct DhtStats {
12    /// Total number of active registrations.
13    pub total_registrations: usize,
14    /// Number of unique DHT keys with registrations.
15    pub unique_keys: usize,
16    /// Number of unique trust roots.
17    pub unique_trust_roots: usize,
18    /// Maximum registrations at any single key.
19    ///
20    /// This counts materialized copies, so the largest bucket is ordinarily an
21    /// ancestor key holding a whole subtree. It is an observation and not a
22    /// limit: nothing bounds it, and it routinely exceeds
23    /// [`SimulationConfig::max_registrations_per_capability`], which bounds
24    /// only the key an agent registers at.
25    ///
26    /// [`SimulationConfig::max_registrations_per_capability`]: crate::SimulationConfig::max_registrations_per_capability
27    pub max_registrations_per_key: usize,
28    /// Average registrations per key.
29    pub avg_registrations_per_key: f64,
30    /// Histogram of path depths.
31    pub path_depth_histogram: Vec<usize>,
32    /// Estimated memory usage in bytes.
33    pub memory_bytes: usize,
34}
35
36impl DhtStats {
37    /// Creates empty stats.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Returns the total registrations.
44    #[must_use]
45    pub const fn total_registrations(&self) -> usize {
46        self.total_registrations
47    }
48
49    /// Returns the unique keys count.
50    #[must_use]
51    pub const fn unique_keys(&self) -> usize {
52        self.unique_keys
53    }
54
55    /// Returns the unique trust roots count.
56    #[must_use]
57    pub const fn unique_trust_roots(&self) -> usize {
58        self.unique_trust_roots
59    }
60
61    /// Returns the maximum registrations at any key.
62    ///
63    /// See [`DhtStats::max_registrations_per_key`] for why this is not bounded
64    /// by the configured capacity.
65    #[must_use]
66    pub const fn max_registrations_per_key(&self) -> usize {
67        self.max_registrations_per_key
68    }
69
70    /// Returns the average registrations per key.
71    #[must_use]
72    pub const fn avg_registrations_per_key(&self) -> f64 {
73        self.avg_registrations_per_key
74    }
75
76    /// Returns the memory usage estimate.
77    #[must_use]
78    pub const fn memory_bytes(&self) -> usize {
79        self.memory_bytes
80    }
81}
82
83/// Result of a simulated migration operation.
84///
85/// Captures the before/after state and performance metrics.
86#[derive(Debug, Clone)]
87pub struct MigrationResult {
88    /// The agent URI that was migrated.
89    pub agent_uri: String,
90    /// The old endpoint(s).
91    pub old_endpoints: Vec<Endpoint>,
92    /// The new endpoint(s).
93    pub new_endpoints: Vec<Endpoint>,
94    /// Time taken to complete the update.
95    pub update_latency: Duration,
96    /// Whether the migration was successful.
97    pub success: bool,
98}
99
100impl MigrationResult {
101    /// Creates a successful migration result.
102    #[must_use]
103    pub fn success(
104        agent_uri: impl Into<String>,
105        old_endpoints: Vec<Endpoint>,
106        new_endpoints: Vec<Endpoint>,
107        update_latency: Duration,
108    ) -> Self {
109        Self {
110            agent_uri: agent_uri.into(),
111            old_endpoints,
112            new_endpoints,
113            update_latency,
114            success: true,
115        }
116    }
117
118    /// Returns true if the migration succeeded.
119    #[must_use]
120    pub const fn is_success(&self) -> bool {
121        self.success
122    }
123
124    /// Returns the agent URI.
125    #[must_use]
126    pub fn agent_uri(&self) -> &str {
127        &self.agent_uri
128    }
129
130    /// Returns the old endpoints.
131    #[must_use]
132    pub fn old_endpoints(&self) -> &[Endpoint] {
133        &self.old_endpoints
134    }
135
136    /// Returns the new endpoints.
137    #[must_use]
138    pub fn new_endpoints(&self) -> &[Endpoint] {
139        &self.new_endpoints
140    }
141
142    /// Returns the update latency.
143    #[must_use]
144    pub const fn update_latency(&self) -> Duration {
145        self.update_latency
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn default_stats() {
155        let stats = DhtStats::default();
156        assert_eq!(stats.total_registrations(), 0);
157        assert_eq!(stats.unique_keys(), 0);
158        assert_eq!(stats.unique_trust_roots(), 0);
159        assert_eq!(stats.max_registrations_per_key(), 0);
160        assert!(
161            (stats.avg_registrations_per_key() - 0.0).abs() < f64::EPSILON,
162            "avg_registrations_per_key should be 0.0"
163        );
164        assert_eq!(stats.memory_bytes(), 0);
165    }
166
167    #[test]
168    fn successful_migration() {
169        let old = vec![Endpoint::https("old.example.com")];
170        let new = vec![Endpoint::https("new.example.com")];
171        let result = MigrationResult::success(
172            "agent://example.com/test/agent_123",
173            old.clone(),
174            new.clone(),
175            Duration::from_millis(50),
176        );
177
178        assert!(result.is_success());
179        assert_eq!(result.agent_uri(), "agent://example.com/test/agent_123");
180        assert_eq!(result.old_endpoints(), &old);
181        assert_eq!(result.new_endpoints(), &new);
182        assert_eq!(result.update_latency(), Duration::from_millis(50));
183    }
184}