mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 08:14:42 +00:00
Introduce a read-snapshot plane so pure-snapshot control queries render in the control-socket task instead of round-tripping the rx_loop, removing the head-of-line coupling that let a busy or slow rx_loop time out fipsctl and fipstop observability. - ControlReadHandle: a cloneable bundle the control accept loop holds, over the node's already-shared NodeContext and MetricsRegistry plus an ArcSwap-published StatsSnapshot. A snapshot_dispatch seam serves cut-over commands off-loop and falls through to the rx_loop for the rest, keeping the rx_loop's ownership of Node intact. - StatsSnapshot is published from the tick (the natural and sole mutator of stats_history), carrying the history rings plus the scalar gauges and counts show_status reports. Readers serve the latest snapshot unconditionally, with staleness bounded by the tick interval and no IO_TIMEOUT-coupled fallback. - Off-loop now: show_status, show_stats_history, show_stats_all_history, show_listening_sockets, show_stats_list, and a new counter-only show_metrics (exposed as fipsctl "stats metrics", the enabler for a Prometheus scraper at no hot-path cost). Queries that need live per-entity state (peers, links, sessions, routing, and the per-peer stats variants) stay on the rx_loop path pending later phases. Quartet green; forward-merge to next verified clean.
80 lines
3.1 KiB
Rust
80 lines
3.1 KiB
Rust
//! Read-side state snapshots published from the node's natural mutators so
|
|
//! pure-snapshot `show_*` queries render off the rx_loop hot path.
|
|
//!
|
|
//! [`StatsSnapshot`] is the R2 reference implementation of the canonical
|
|
//! snapshot pattern (see `design/fast-path-refactoring-r0-read-handle.md`): a
|
|
//! read-only data bundle published via `ArcSwap` from the tick after
|
|
//! `StatsHistory::tick()`. It carries
|
|
//!
|
|
//! - the `stats_history` read-side rings (the "dual-ring": the live mutable
|
|
//! ring stays on the tick; this is the cloned read copy), and
|
|
//! - the cheap scalar gauges `show_status` needs (`estimated_mesh_size`,
|
|
//! node `state`, `tun_state`, `tun_name`, `effective_ipv6_mtu`, and the
|
|
//! peer / session / link / connection / transport counts), plus
|
|
//! `peer_aliases` (effectively immutable after construction).
|
|
//!
|
|
//! The snapshot holds *data*, not rendered `Response` envelopes (Q1-d):
|
|
//! rendering happens in the control task off the rx_loop. Staleness is bounded
|
|
//! by the tick interval and is never staler than the underlying data, which
|
|
//! also advances only on the tick (Q1-b).
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use crate::identity::NodeAddr;
|
|
use crate::node::NodeState;
|
|
use crate::node::stats_history::StatsHistory;
|
|
use crate::upper::tun::TunState;
|
|
|
|
/// Read-only snapshot of the stats-history rings plus the scalar gauges and
|
|
/// counts `show_status` reports. Published from the tick (Q1-b).
|
|
#[derive(Clone)]
|
|
pub(crate) struct StatsSnapshot {
|
|
/// Cloned read copy of the history rings (the dual-ring read side).
|
|
pub history: Arc<StatsHistory>,
|
|
/// Cached estimated mesh size, or `None` when no estimate is available.
|
|
pub estimated_mesh_size: Option<u64>,
|
|
/// Node operational state.
|
|
pub state: NodeState,
|
|
/// TUN device state.
|
|
pub tun_state: TunState,
|
|
/// TUN interface name, if active.
|
|
pub tun_name: Option<String>,
|
|
/// Effective IPv6 MTU over the mesh.
|
|
pub effective_ipv6_mtu: u16,
|
|
/// Number of pending connections (handshake in progress).
|
|
pub connection_count: usize,
|
|
/// Number of authenticated peers.
|
|
pub peer_count: usize,
|
|
/// Number of active links.
|
|
pub link_count: usize,
|
|
/// Number of active transports.
|
|
pub transport_count: usize,
|
|
/// Number of active sessions.
|
|
pub session_count: usize,
|
|
/// Configured peer aliases, keyed by `NodeAddr`. Effectively immutable
|
|
/// after construction; shared to avoid a per-tick map clone.
|
|
pub peer_aliases: Arc<HashMap<NodeAddr, String>>,
|
|
}
|
|
|
|
impl StatsSnapshot {
|
|
/// Build an empty snapshot for seeding the `ArcSwap` cell at construction,
|
|
/// before the first tick has published real state.
|
|
pub(crate) fn empty() -> Self {
|
|
Self {
|
|
history: Arc::new(StatsHistory::new()),
|
|
estimated_mesh_size: None,
|
|
state: NodeState::Created,
|
|
tun_state: TunState::Disabled,
|
|
tun_name: None,
|
|
effective_ipv6_mtu: 0,
|
|
connection_count: 0,
|
|
peer_count: 0,
|
|
link_count: 0,
|
|
transport_count: 0,
|
|
session_count: 0,
|
|
peer_aliases: Arc::new(HashMap::new()),
|
|
}
|
|
}
|
|
}
|