From c5f67f56f46ea5c0b300a2e0eb00a7368efe4f2a Mon Sep 17 00:00:00 2001 From: tv Date: Thu, 5 Nov 2026 02:58:27 +0100 Subject: dashboard: add epoch switch indicator --- src/remake/mod.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'src/remake/mod.rs') diff --git a/src/remake/mod.rs b/src/remake/mod.rs index 2273456..b16b8a3 100644 --- a/src/remake/mod.rs +++ b/src/remake/mod.rs @@ -88,12 +88,21 @@ pub(crate) struct Current { pub(crate) voters: Vec, /// The epoch's generation it remakes: a member past it switched. pub(crate) from_generation: u64, + /// Committed: the wait between seeing the commit and switching + /// (`Proposal::switch_delay_ms`), and how much of it is left on this node (ms). + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) switch_delay_ms: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) switch_in_ms: Option, } /// The remakes of `epoch` in `doc`, with their phase at `now` (expiry by `grace`, this -/// node's for records without their own: `Record::phase`). +/// node's `dormant_after`, for records without their own: `Record::phase`), and for one +/// committed, this node's wait to switch (`remake`: when it saw the commit; as +/// `switch::check` waits: the proposal's delay, or a quarter of `grace`). #[cfg_attr(not(feature = "dashboard"), allow(dead_code))] pub(crate) fn current( + remake: &RemakeState, doc: &Document, epoch: Epoch, now: crate::clock::UnixMillis, @@ -103,12 +112,23 @@ pub(crate) fn current( records::load_all(doc) .into_iter() .filter(|(_, record)| record.proposal.from_epoch == local) - .map(|(id, record)| Current { - phase: record.phase(now, grace), - coordinator: record.proposal.coordinator.clone(), - voters: record.votes.keys().cloned().collect(), - from_generation: local.generation, - id, + .map(|(id, record)| { + let phase = record.phase(now, grace); + let delay = (phase == records::Phase::Committed) + .then(|| record.proposal.switch_delay_ms.unwrap_or(grace / 4)); + let left = delay.and_then(|delay| { + let seen = remake.seen_for(&id)?; + Some(delay.saturating_sub(seen.as_millis() as u64)) + }); + Current { + phase, + coordinator: record.proposal.coordinator.clone(), + voters: record.votes.keys().cloned().collect(), + from_generation: local.generation, + switch_delay_ms: delay, + switch_in_ms: left, + id, + } }) .collect() } -- cgit v1.3.1