Skip to main content

prospero_core/
metrics.rs

1//! Process-lifetime operational counters for prosperod.
2//!
3//! ADR-0004 specifies a failed `Store.append` is "logged **and metered**", and
4//! `caliband::stream` documents an unknown frame as something the caller should
5//! "log **+ count**". These counters meter both, plus a little surrounding
6//! signal (events appended, repos polled, active attaches), so operators aren't
7//! blind to persistence loss, protocol drift, or attach load. They are exposed
8//! over the API via [`MetricsSnapshot`].
9
10use std::sync::atomic::{AtomicU64, Ordering};
11
12use serde::Serialize;
13
14/// Monotonic operational counters, incremented from the hot paths. Cheap,
15/// lock-free, and shared (behind an `Arc`) across the manager and its tasks.
16#[derive(Debug, Default)]
17pub struct Metrics {
18    events_appended: AtomicU64,
19    append_failures: AtomicU64,
20    unknown_frames: AtomicU64,
21    repos_polled: AtomicU64,
22}
23
24impl Metrics {
25    /// A `Store.append` succeeded.
26    pub(crate) fn record_append_ok(&self) {
27        self.events_appended.fetch_add(1, Ordering::Relaxed);
28    }
29
30    /// A `Store.append` failed (durability loss; see ADR-0004).
31    pub(crate) fn record_append_failure(&self) {
32        self.append_failures.fetch_add(1, Ordering::Relaxed);
33    }
34
35    /// An unrecognized caliban stream frame was seen (protocol drift).
36    pub(crate) fn record_unknown_frame(&self) {
37        self.unknown_frames.fetch_add(1, Ordering::Relaxed);
38    }
39
40    /// One repo poll cycle ran.
41    pub(crate) fn record_repo_poll(&self) {
42        self.repos_polled.fetch_add(1, Ordering::Relaxed);
43    }
44
45    /// Read the counters into a serializable snapshot. `active_attaches` is a
46    /// gauge supplied by the caller (the current attach-task count).
47    pub fn snapshot(&self, active_attaches: u64) -> MetricsSnapshot {
48        MetricsSnapshot {
49            events_appended: self.events_appended.load(Ordering::Relaxed),
50            append_failures: self.append_failures.load(Ordering::Relaxed),
51            unknown_frames: self.unknown_frames.load(Ordering::Relaxed),
52            repos_polled: self.repos_polled.load(Ordering::Relaxed),
53            active_attaches,
54        }
55    }
56}
57
58/// A point-in-time snapshot of prosperod's operational counters, returned by
59/// `GET /api/metrics`.
60#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
61pub struct MetricsSnapshot {
62    /// Events successfully appended to the durable store.
63    pub events_appended: u64,
64    /// `Store.append` failures (durability loss).
65    pub append_failures: u64,
66    /// Unrecognized caliban stream frames (protocol drift).
67    pub unknown_frames: u64,
68    /// Repo poll cycles run.
69    pub repos_polled: u64,
70    /// Attach tasks currently running (a gauge, not a counter).
71    pub active_attaches: u64,
72}