diff --git a/src/node/mod.rs b/src/node/mod.rs index c308633..d24b692 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -1178,8 +1178,6 @@ impl Node { /// Returning the smallest (rather than the first-iterated, which used /// to vary across HashMap iteration order + async-startup race) makes /// the clamp deterministic across daemon restarts. - /// - /// See `ISSUE-2026-0011` for the empirical investigation. pub fn transport_mtu(&self) -> u16 { let min_operational = self .transports @@ -1454,14 +1452,14 @@ impl Node { self.stats_history.tick(now, &snap, &peer_snaps); - // Publish the read-side snapshot (R2 dual-ring, Q1-b). The tick is the + // Publish the read copy of the dual-ring stats snapshot. The tick is the // natural and sole mutator of `stats_history`, so publishing here can // never produce false staleness: the snapshot and the underlying data - // advance together. This is data, not a rendered response (Q1-d), and - // it is published only here, not in a monolithic per-tick rebuild of - // every query (Q1-c). It also is not gated behind any slow I/O on the - // tick the way the abandoned 2edc8a1 republish was. - // Per-stats-history-peer metadata (R5). `show_stats_peers` / + // advance together. What is published is data, not a rendered response, + // and it is published only here, rather than as a monolithic per-tick + // rebuild of every query's result. It also is not gated behind any slow + // I/O on the tick the way the abandoned 2edc8a1 republish was. + // Per-stats-history-peer metadata. `show_stats_peers` / // `show_stats_history_all_peers` need each tracked peer's live // membership (`is_active`), resolved npub, and display name — all // cross-subsystem reads against the live peer table and host map, @@ -1565,21 +1563,21 @@ impl Node { /// publish it via `ArcSwap`, so `show_tree` / `show_bloom` / `show_cache` /// / `show_routing` / `show_identity_cache` render off the rx_loop. /// - /// **Q1 publisher placement.** The four projected subsystems (tree / bloom + /// **Publisher placement.** The four projected subsystems (tree / bloom /// / coord cache / identity cache) mutate at dozens of scattered handler /// sites, and every projected row carries a *display name* resolved against /// the live peer/session tables and host map — state reachable only with - /// `&Node`. Per-mutator on-change publication (Q1-a) would therefore be - /// large, error-prone surgery, and each call would still need `&Node` to - /// resolve names across subsystem boundaries. So this projection is - /// published from the tick — the documented acceptable interim (the spec's - /// "publish from the tick" allowance, mirroring R2's stats publish). The - /// tick is the one site with coherent `&Node` access to resolve every - /// display name together. A single combined cell is the natural shape - /// because there is exactly one publisher, so the multi-mutator - /// whole-snapshot-rebuild hazard Q1-c warns against does not arise. + /// `&Node`. Publishing on change from each individual mutator would + /// therefore be large, error-prone surgery, and each call would still need + /// `&Node` to resolve names across subsystem boundaries. So this projection + /// is published from the tick instead, the same placement the stats + /// snapshot above uses. The tick is the one site with coherent `&Node` + /// access to resolve every display name together. A single combined cell is + /// the natural shape because there is exactly one publisher, so the + /// whole-snapshot-rebuild hazard that afflicts multi-mutator designs does + /// not arise. /// - /// The snapshot holds typed rows + scalars (Q1-d data, not rendered + /// The snapshot holds typed rows + scalars (data, not rendered /// responses); the counter-family `stats` blocks the queries also emit are /// served from the `MetricsRegistry` (already `Arc`-shared) at render time. fn publish_routing_snapshot(&self) { @@ -1768,19 +1766,20 @@ impl Node { /// `show_connections` / `show_transports` / `show_mmp` render off the /// rx_loop. /// - /// **Q1 publisher placement (tick, like R3).** Every projected row needs a - /// display name resolved against the live peer/session tables and host map + /// **Publisher placement (from the tick, as with the routing snapshot + /// above).** Every projected row needs a display name resolved against the + /// live peer/session tables and host map /// (`&Node`); `show_peers` additionally needs the live tree state to derive /// `is_parent` / `is_child` plus the Nostr-discovery failure-state map — /// cross-subsystem reads available only with `&Node`. And most fields /// (link/session traffic counters, MMP metrics, `last_seen`, noise counters) /// mutate continuously on the data plane, not at the discrete entity - /// lifecycle mutators, so per-lifecycle-mutator publication (Q1-a) would not - /// capture their freshness anyway. The tick is the natural cadence with - /// coherent `&Node` access. + /// lifecycle mutators, so publishing on change from each lifecycle mutator + /// would not capture their freshness anyway. The tick is the natural + /// cadence with coherent `&Node` access. /// - /// **Structural sharing (the R4 umbrella mandate).** Each table is a - /// `Vec>`. The freshly-projected rows are reconciled against the + /// **Structural sharing.** Each table is a `Vec>`. + /// The freshly-projected rows are reconciled against the /// previously published snapshot via /// [`reconcile_rows`](crate::control::snapshot::reconcile_rows): a row's /// `Arc` is reused (kept by pointer) whenever it matches the prior row by diff --git a/src/proto/fmp/core.rs b/src/proto/fmp/core.rs index d134974..11bab16 100644 --- a/src/proto/fmp/core.rs +++ b/src/proto/fmp/core.rs @@ -317,9 +317,11 @@ pub(crate) enum ConnAction { /// Read-only view of FMP connection/peer state the lifecycle core needs. /// /// The core defines this interface; the async shell (`node`) implements it over -/// the live `connections`/`peers` maps. It is a **snapshot-iterator** seam: -/// each method returns owned snapshot vectors with all clock reads already -/// resolved shell-side, so the pure decisions never borrow `Node` and never +/// the live `peer_machines`/`peers` maps — handshake-phase state is read off +/// the machines still carrying a pending handshake, active-peer state off +/// `peers`. It is a **snapshot-iterator** seam: each method returns owned +/// snapshot vectors with all clock reads already resolved shell-side, so the +/// pure decisions never borrow `Node` and never /// read a clock. Keeping it a trait keeps `proto` free of a `node` dependency /// and lets the decisions be unit-tested against hand-built snapshots. pub(crate) trait LifecycleView { @@ -430,8 +432,8 @@ const REKEY_MIN_SESSION_AGE_SECS: u64 = 30; /// needs about a peer whose msg1 has just been processed. /// /// The core defines this interface; the async shell (`node`) implements it over -/// the live `peers`/`connections` maps, resolving every clock read into a plain -/// `u64` before the [`EstablishSnapshot`] reaches the core. Keeping it a trait +/// the live `peers` map, resolving every clock read into a plain `u64` before +/// the [`EstablishSnapshot`] reaches the core. Keeping it a trait /// keeps `proto` free of a `node` dependency and lets the establish decision be /// unit-tested against hand-built snapshots. pub(crate) trait EstablishView {