mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 16:43:12 +00:00
node: extract immutable state into a shared context and atomic metric registry
Store node counters in an atomic metric registry read through &self, and introduce a shared NodeContext bundle holding the effectively-immutable fields (config, identity, startup epoch, node profile, capability limits). Source the immutable config and identity reads across the receive hot path, the XX handshake/session/rekey state machines, and the discovery, tree, bloom, retry, and lifecycle modules through the context accessors. Includes the bloom delta/full/NACK/resize and byte-total counters in the registry. The Node fields and the context are rebuilt in lockstep at every mutation site.
This commit is contained in:
+84
-13
@@ -6,6 +6,7 @@
|
||||
|
||||
mod acl;
|
||||
mod bloom;
|
||||
mod context;
|
||||
#[cfg(unix)]
|
||||
pub(crate) mod decrypt_worker;
|
||||
mod discovery_rate_limit;
|
||||
@@ -13,6 +14,7 @@ mod discovery_rate_limit;
|
||||
pub(crate) mod encrypt_worker;
|
||||
mod handlers;
|
||||
mod lifecycle;
|
||||
pub(crate) mod metrics;
|
||||
mod rate_limit;
|
||||
pub(crate) mod reject;
|
||||
mod reloadable;
|
||||
@@ -319,6 +321,13 @@ pub struct Node {
|
||||
/// Loaded configuration.
|
||||
config: Config,
|
||||
|
||||
/// Shared immutable context bundle. A parallel, authoritative copy of the
|
||||
/// effectively-immutable fields (config/identity/startup_epoch/started_at/
|
||||
/// is_leaf_only/max_*), kept in lockstep with the `Node` fields via
|
||||
/// `rebuild_context`. Readers migrate onto it in later sub-PRs; the
|
||||
/// duplicated `Node` fields are removed once the last reader has moved.
|
||||
context: Arc<context::NodeContext>,
|
||||
|
||||
// === State ===
|
||||
/// Node operational state.
|
||||
state: NodeState,
|
||||
@@ -413,6 +422,10 @@ pub struct Node {
|
||||
/// Routing, forwarding, discovery, and error signal counters.
|
||||
stats: stats::NodeStats,
|
||||
|
||||
/// Lock-free atomic metric counters. Shadows `stats` during the
|
||||
/// counter migration; bumped alongside it with a parity check.
|
||||
metrics: std::sync::Arc<metrics::MetricsRegistry>,
|
||||
|
||||
/// Time-series history of node-level metrics (1s/1m rings).
|
||||
stats_history: stats_history::StatsHistory,
|
||||
|
||||
@@ -649,11 +662,25 @@ impl Node {
|
||||
let (decrypt_fallback_tx, decrypt_fallback_rx) =
|
||||
tokio::sync::mpsc::unbounded_channel::<decrypt_worker::DecryptWorkerEvent>();
|
||||
|
||||
let started_at = std::time::Instant::now();
|
||||
let context = Arc::new(context::NodeContext::new(
|
||||
Arc::new(config.clone()),
|
||||
identity.clone(),
|
||||
startup_epoch,
|
||||
started_at,
|
||||
is_leaf_only,
|
||||
node_profile,
|
||||
max_connections,
|
||||
max_peers,
|
||||
max_links,
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
identity,
|
||||
startup_epoch,
|
||||
started_at: std::time::Instant::now(),
|
||||
started_at,
|
||||
config,
|
||||
context,
|
||||
state: NodeState::Created,
|
||||
is_leaf_only,
|
||||
node_profile,
|
||||
@@ -679,6 +706,7 @@ impl Node {
|
||||
next_link_id: 1,
|
||||
next_transport_id: 1,
|
||||
stats: stats::NodeStats::new(),
|
||||
metrics: std::sync::Arc::new(metrics::MetricsRegistry::new()),
|
||||
stats_history: stats_history::StatsHistory::new(),
|
||||
tun_state,
|
||||
tun_name: None,
|
||||
@@ -797,11 +825,25 @@ impl Node {
|
||||
let (decrypt_fallback_tx, decrypt_fallback_rx) =
|
||||
tokio::sync::mpsc::unbounded_channel::<decrypt_worker::DecryptWorkerEvent>();
|
||||
|
||||
let started_at = std::time::Instant::now();
|
||||
let context = Arc::new(context::NodeContext::new(
|
||||
Arc::new(config.clone()),
|
||||
identity.clone(),
|
||||
startup_epoch,
|
||||
started_at,
|
||||
false,
|
||||
NodeProfile::Full,
|
||||
max_connections,
|
||||
max_peers,
|
||||
max_links,
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
identity,
|
||||
startup_epoch,
|
||||
started_at: std::time::Instant::now(),
|
||||
started_at,
|
||||
config,
|
||||
context,
|
||||
state: NodeState::Created,
|
||||
is_leaf_only: false,
|
||||
node_profile: NodeProfile::Full,
|
||||
@@ -827,6 +869,7 @@ impl Node {
|
||||
next_link_id: 1,
|
||||
next_transport_id: 1,
|
||||
stats: stats::NodeStats::new(),
|
||||
metrics: std::sync::Arc::new(metrics::MetricsRegistry::new()),
|
||||
stats_history: stats_history::StatsHistory::new(),
|
||||
tun_state,
|
||||
tun_name: None,
|
||||
@@ -886,6 +929,7 @@ impl Node {
|
||||
node.is_leaf_only = true;
|
||||
node.node_profile = NodeProfile::Leaf;
|
||||
node.bloom_state = BloomState::leaf_only(*node.identity.node_addr());
|
||||
node.rebuild_context();
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
@@ -1100,7 +1144,7 @@ impl Node {
|
||||
|
||||
/// Get this node's identity.
|
||||
pub fn identity(&self) -> &Identity {
|
||||
&self.identity
|
||||
&self.context.identity
|
||||
}
|
||||
|
||||
/// Get this node's NodeAddr.
|
||||
@@ -1150,7 +1194,26 @@ impl Node {
|
||||
|
||||
/// Get the configuration.
|
||||
pub fn config(&self) -> &Config {
|
||||
&self.config
|
||||
self.context.config.as_ref()
|
||||
}
|
||||
|
||||
/// Rebuild the shared [`context::NodeContext`] from the current `Node`
|
||||
/// fields. Called after any mutation of a bundled field (`update_peers`,
|
||||
/// the `set_max_*` setters) so `self.context` stays equal to the `Node`
|
||||
/// fields it mirrors. Cheap — the only deep copy is the (rare) `Config`
|
||||
/// clone.
|
||||
fn rebuild_context(&mut self) {
|
||||
self.context = Arc::new(context::NodeContext::new(
|
||||
Arc::new(self.config.clone()),
|
||||
self.identity.clone(),
|
||||
self.startup_epoch,
|
||||
self.started_at,
|
||||
self.is_leaf_only,
|
||||
self.node_profile,
|
||||
self.max_connections,
|
||||
self.max_peers,
|
||||
self.max_links,
|
||||
));
|
||||
}
|
||||
|
||||
/// Calculate the effective IPv6 MTU that can be sent over FIPS.
|
||||
@@ -1204,7 +1267,7 @@ impl Node {
|
||||
|
||||
/// Get the node uptime.
|
||||
pub fn uptime(&self) -> std::time::Duration {
|
||||
self.started_at.elapsed()
|
||||
self.context.started_at.elapsed()
|
||||
}
|
||||
|
||||
/// Check if node is operational.
|
||||
@@ -1214,12 +1277,12 @@ impl Node {
|
||||
|
||||
/// Check if this is a leaf-only node.
|
||||
pub fn is_leaf_only(&self) -> bool {
|
||||
self.is_leaf_only
|
||||
self.context.is_leaf_only
|
||||
}
|
||||
|
||||
/// Get the node's profile (Full, NonRouting, Leaf).
|
||||
pub fn node_profile(&self) -> NodeProfile {
|
||||
self.node_profile
|
||||
self.context.node_profile
|
||||
}
|
||||
|
||||
/// Collect the set of peers that are not full nodes (non-routing/leaf).
|
||||
@@ -1376,6 +1439,11 @@ impl Node {
|
||||
&mut self.stats
|
||||
}
|
||||
|
||||
/// Get the atomic metric registry.
|
||||
pub(crate) fn metrics(&self) -> &metrics::MetricsRegistry {
|
||||
&self.metrics
|
||||
}
|
||||
|
||||
/// Get the stats history collector.
|
||||
pub fn stats_history(&self) -> &stats_history::StatsHistory {
|
||||
&self.stats_history
|
||||
@@ -1384,7 +1452,7 @@ impl Node {
|
||||
/// 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 fwd = &self.metrics.forwarding;
|
||||
let peers_with_mmp: Vec<f64> = self
|
||||
.peers
|
||||
.values()
|
||||
@@ -1400,11 +1468,11 @@ impl Node {
|
||||
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,
|
||||
parent_switches_total: self.metrics.tree.parent_switches.get(),
|
||||
bytes_in_total: fwd.received_bytes.get(),
|
||||
bytes_out_total: fwd.forwarded_bytes.get() + fwd.originated_bytes.get(),
|
||||
packets_in_total: fwd.received_packets.get(),
|
||||
packets_out_total: fwd.forwarded_packets.get() + fwd.originated_packets.get(),
|
||||
loss_rate,
|
||||
active_sessions: self.sessions.len() as u64,
|
||||
};
|
||||
@@ -1457,11 +1525,13 @@ impl Node {
|
||||
/// Set the maximum number of connections (handshake phase).
|
||||
pub fn set_max_connections(&mut self, max: usize) {
|
||||
self.max_connections = max;
|
||||
self.rebuild_context();
|
||||
}
|
||||
|
||||
/// Set the maximum number of peers (authenticated).
|
||||
pub fn set_max_peers(&mut self, max: usize) {
|
||||
self.max_peers = max;
|
||||
self.rebuild_context();
|
||||
}
|
||||
|
||||
/// Returns false when we are at or above the configured `max_peers`
|
||||
@@ -1479,6 +1549,7 @@ impl Node {
|
||||
/// Set the maximum number of links.
|
||||
pub fn set_max_links(&mut self, max: usize) {
|
||||
self.max_links = max;
|
||||
self.rebuild_context();
|
||||
}
|
||||
|
||||
// === Counts ===
|
||||
|
||||
Reference in New Issue
Block a user