summaryrefslogtreecommitdiffstats
path: root/src/remake
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-26 20:48:08 +0100
committertv <tv@krebsco.de>2026-10-26 20:48:08 +0100
commit8414f96f27b059c4536ae67ddfce18dec54e7451 (patch)
tree49d0aadd01b12f03242817c662c938b75e2efbb3 /src/remake
parent53baaff5d7c1c9fd62cf0e94bcb86120edd699b6 (diff)
add remake stats
Diffstat (limited to 'src/remake')
-rw-r--r--src/remake/local.rs5
-rw-r--r--src/remake/mod.rs42
2 files changed, 47 insertions, 0 deletions
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<ArchivedEpoch> {
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<crate::clock::UnixMillis>,
+ 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.