Merge branch 'master' into next

This commit is contained in:
Johnathan Corgan
2026-04-14 09:34:05 +00:00
19 changed files with 3076 additions and 28 deletions
+1 -1
View File
@@ -618,7 +618,7 @@ impl Node {
}
/// Tracks a pending discovery lookup with retry state.
pub(crate) struct PendingLookup {
pub struct PendingLookup {
/// When the lookup was first initiated.
pub initiated_ms: u64,
/// When the last attempt was sent.
+2 -1
View File
@@ -102,7 +102,7 @@ impl Node {
}
Some((request, response_tx)) = control_rx.recv() => {
let response = if request.command.starts_with("show_") {
queries::dispatch(self, &request.command)
queries::dispatch(self, &request.command, request.params.as_ref())
} else {
commands::dispatch(
self,
@@ -127,6 +127,7 @@ impl Node {
self.check_tree_state().await;
self.check_bloom_state().await;
self.compute_mesh_size();
self.record_stats_history();
self.check_mmp_reports().await;
self.check_session_mmp_reports().await;
self.check_link_heartbeats().await;
+109
View File
@@ -14,6 +14,7 @@ mod routing_error_rate_limit;
pub(crate) mod session;
pub(crate) mod session_wire;
pub(crate) mod stats;
pub(crate) mod stats_history;
#[cfg(test)]
mod tests;
mod tree;
@@ -363,6 +364,9 @@ pub struct Node {
/// Routing, forwarding, discovery, and error signal counters.
stats: stats::NodeStats,
/// Time-series history of node-level metrics (1s/1m rings).
stats_history: stats_history::StatsHistory,
// === TUN Interface ===
/// TUN device state.
tun_state: TunState,
@@ -546,6 +550,7 @@ impl Node {
next_link_id: 1,
next_transport_id: 1,
stats: stats::NodeStats::new(),
stats_history: stats_history::StatsHistory::new(),
tun_state,
tun_name: None,
tun_tx: None,
@@ -657,6 +662,7 @@ impl Node {
next_link_id: 1,
next_transport_id: 1,
stats: stats::NodeStats::new(),
stats_history: stats_history::StatsHistory::new(),
tun_state,
tun_name: None,
tun_tx: None,
@@ -1143,6 +1149,70 @@ impl Node {
&mut self.stats
}
/// Get the stats history collector.
pub fn stats_history(&self) -> &stats_history::StatsHistory {
&self.stats_history
}
/// Sample the current node state into the stats history ring.
/// Called once per tick from the RX loop.
pub(crate) fn record_stats_history(&mut self) {
let fwd = &self.stats.forwarding;
let peers_with_mmp: Vec<f64> = self
.peers
.values()
.filter_map(|p| p.mmp().map(|m| m.metrics.loss_rate()))
.collect();
let loss_rate = if peers_with_mmp.is_empty() {
0.0
} else {
peers_with_mmp.iter().sum::<f64>() / peers_with_mmp.len() as f64
};
let snap = stats_history::Snapshot {
mesh_size: self.estimated_mesh_size,
tree_depth: self.tree_state.my_coords().depth() as u32,
peer_count: self.peers.len() as u64,
parent_switches_total: self.stats.tree.parent_switches,
bytes_in_total: fwd.received_bytes,
bytes_out_total: fwd.forwarded_bytes + fwd.originated_bytes,
packets_in_total: fwd.received_packets,
packets_out_total: fwd.forwarded_packets + fwd.originated_packets,
loss_rate,
active_sessions: self.sessions.len() as u64,
};
let now = std::time::Instant::now();
let peer_snaps: Vec<stats_history::PeerSnapshot> = self
.peers
.values()
.map(|p| {
let stats = p.link_stats();
let (srtt_ms, loss_rate, ecn_ce) = match p.mmp() {
Some(m) => (
m.metrics.srtt_ms(),
Some(m.metrics.loss_rate()),
m.receiver.ecn_ce_count() as u64,
),
None => (None, None, 0),
};
stats_history::PeerSnapshot {
node_addr: *p.node_addr(),
last_seen: now,
srtt_ms,
loss_rate,
bytes_in_total: stats.bytes_recv,
bytes_out_total: stats.bytes_sent,
packets_in_total: stats.packets_recv,
packets_out_total: stats.packets_sent,
ecn_ce_total: ecn_ce,
}
})
.collect();
self.stats_history.tick(now, &snap, &peer_snaps);
}
// === TUN Interface ===
/// Get the TUN state.
@@ -1453,16 +1523,55 @@ impl Node {
self.identity_cache.len()
}
/// Iterate over identity cache entries.
///
/// Returns `(NodeAddr, PublicKey, last_seen_ms)` for each cached identity.
/// Used by the `show_identity_cache` control query.
pub fn identity_cache_iter(
&self,
) -> impl Iterator<Item = (&NodeAddr, &secp256k1::PublicKey, u64)> {
self.identity_cache
.values()
.map(|(addr, pk, ts)| (addr, pk, *ts))
}
/// Configured maximum identity cache size.
pub fn identity_cache_max(&self) -> usize {
self.config.node.cache.identity_size
}
/// Number of pending discovery lookups.
pub fn pending_lookup_count(&self) -> usize {
self.pending_lookups.len()
}
/// Iterate over pending discovery lookups for diagnostics.
pub fn pending_lookups_iter(
&self,
) -> impl Iterator<Item = (&NodeAddr, &handlers::discovery::PendingLookup)> {
self.pending_lookups.iter()
}
/// Number of recent discovery requests tracked.
pub fn recent_request_count(&self) -> usize {
self.recent_requests.len()
}
/// Count of destinations with queued TUN packets awaiting session setup.
pub fn pending_tun_destinations(&self) -> usize {
self.pending_tun_packets.len()
}
/// Total TUN packets queued across all destinations.
pub fn pending_tun_total_packets(&self) -> usize {
self.pending_tun_packets.values().map(|q| q.len()).sum()
}
/// Iterate over retry state for diagnostics.
pub fn retry_state_iter(&self) -> impl Iterator<Item = (&NodeAddr, &retry::RetryState)> {
self.retry_pending.iter()
}
// === Routing ===
/// Check if a peer is a tree neighbor (parent or child in the spanning tree).
File diff suppressed because it is too large Load Diff