From 8414f96f27b059c4536ae67ddfce18dec54e7451 Mon Sep 17 00:00:00 2001 From: tv Date: Mon, 26 Oct 2026 20:48:08 +0100 Subject: add remake stats --- src/remake/local.rs | 5 +++++ src/remake/mod.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'src/remake') diff --git a/src/remake/local.rs b/src/remake/local.rs index fd7c567..60f7184 100644 --- a/src/remake/local.rs +++ b/src/remake/local.rs @@ -379,6 +379,11 @@ impl RemakeState { self.lock().quarantine.push(quarantined); } + /// How many old epochs are archived. + pub(crate) fn archived_count(&self) -> usize { + self.lock().archive.len() + } + pub(crate) fn archived_epochs(&self) -> Vec { self.lock() .archive diff --git a/src/remake/mod.rs b/src/remake/mod.rs index ad027a7..bd04de9 100644 --- a/src/remake/mod.rs +++ b/src/remake/mod.rs @@ -51,6 +51,48 @@ use tokio::sync::broadcast; use crate::document::replica::Epoch; use crate::swarm::sync::ChangeSet; +/// Remakes and their aftermath on this node, in numbers (`/admin/stats`). +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct Summary { + /// Remakes of the local epoch not decided yet (expired ones included). + pub(crate) undecided: usize, + /// Committed remakes of the local epoch: 1 while switching to it; more is a double + /// commit, on which no node switches by itself. + pub(crate) committed: usize, + pub(crate) rejoining: bool, + /// Attempts at catching up with a newer epoch (0 when not catching up). + pub(crate) catch_up_attempts: u32, + pub(crate) quarantined: usize, + /// When the oldest quarantine entry was made. + pub(crate) oldest_quarantine: Option, + pub(crate) archived_epochs: usize, +} + +/// The `Summary` of `remake` with the records of `doc`, whose epoch is `epoch`. +pub(crate) fn summary(remake: &RemakeState, doc: &AutoCommit, epoch: Epoch) -> Summary { + let local = records::EpochRecord::from(epoch); + let mut summary = Summary { + rejoining: remake.is_rejoining(), + catch_up_attempts: remake.catch_up().map_or(0, |catch_up| catch_up.attempts), + archived_epochs: remake.archived_count(), + ..Summary::default() + }; + for (_, record) in records::load_all(doc) { + if record.proposal.from_epoch != local { + continue; + } + if record.decision.is_none() { + summary.undecided += 1; + } else if record.committed_epoch().is_some() { + summary.committed += 1; + } + } + let created = remake.list_quarantine(|quarantined| quarantined.created_at); + summary.quarantined = created.len(); + summary.oldest_quarantine = created.into_iter().min(); + summary +} + /// What remake needs from the node. Implemented by `AppState`. /// /// Used generically (`H: Host`), never as `dyn Host`, so the generic methods are fine. -- cgit v1.3.1