diff options
| author | tv <tv@krebsco.de> | 2026-11-05 02:58:27 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-05 02:58:27 +0100 |
| commit | c5f67f56f46ea5c0b300a2e0eb00a7368efe4f2a (patch) | |
| tree | e4a9235173e546b008fb222be12862bd0075b96e /src/remake | |
| parent | c10f9144b1436d4918855a657715b453a6228a44 (diff) | |
dashboard: add epoch switch indicator
Diffstat (limited to 'src/remake')
| -rw-r--r-- | src/remake/local.rs | 6 | ||||
| -rw-r--r-- | src/remake/mod.rs | 34 |
2 files changed, 33 insertions, 7 deletions
diff --git a/src/remake/local.rs b/src/remake/local.rs index c80a5a5..e13c6fd 100644 --- a/src/remake/local.rs +++ b/src/remake/local.rs @@ -253,6 +253,12 @@ impl RemakeState { .or_insert_with(Instant::now) } + /// How long ago this node first saw remake `id` committed (`first_seen`), if it did. + #[cfg_attr(not(feature = "dashboard"), allow(dead_code))] + pub(crate) fn seen_for(&self, id: &str) -> Option<std::time::Duration> { + self.lock().first_seen.get(id).map(|at| at.elapsed()) + } + pub(crate) fn status(&self, id: &str) -> Option<LocalStatus> { self.lock().status.get(id).cloned() } 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<String>, /// 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<u64>, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) switch_in_ms: Option<u64>, } /// 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() } |
