Skip to main content

ic_backup/ops/persistence/download_journal/metrics/
mod.rs

1//! Caller-owned host diagnostics using shared arithmetic; no retained authority.
2
3use super::DownloadJournalGuard;
4/// Canonical shared arithmetic for local diagnostic summaries.
5///
6/// Re-exported so callers can name returned summaries without selecting a separate
7/// Metrics dependency. This is the shared type, with no local wrapper or arithmetic.
8pub use ic_metrics::MeasurementSummary;
9use std::{
10    sync::PoisonError,
11    time::{Duration, Instant},
12};
13
14/// Read-only diagnostic snapshot for one opened download-journal guard.
15///
16/// Successful and failed returned calls have separate duration summaries in
17/// nanoseconds. Verification includes internal upload-preparation checks; preparation
18/// durations include those checks and must not be summed with them as exclusive work.
19/// Prepared bytes count successful data payloads only, including empty known chunks;
20/// repeated preparation records another sample, not unique or transferred bytes.
21///
22/// Sampling starts empty on create/open and is never persisted or serialized
23/// or reconstructed from journal evidence. Counts/totals saturate independently;
24/// `u64::MAX` is unavailable for exact interval arithmetic. These values establish
25/// no IC cost, complete transfer, spending, receipt, freshness or outcome authority.
26#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
27pub struct IcSnapshotLocalMetrics {
28    verification_success_ns: MeasurementSummary,
29    verification_failure_ns: MeasurementSummary,
30    upload_metadata_success_ns: MeasurementSummary,
31    upload_metadata_failure_ns: MeasurementSummary,
32    upload_data_success_ns: MeasurementSummary,
33    upload_data_failure_ns: MeasurementSummary,
34    prepared_chunk_bytes: MeasurementSummary,
35}
36
37impl IcSnapshotLocalMetrics {
38    /// Read successful local IC-tree verification durations in nanoseconds.
39    #[must_use]
40    pub const fn verification_success_ns(self) -> MeasurementSummary {
41        self.verification_success_ns
42    }
43    /// Read rejected local IC-tree verification durations in nanoseconds.
44    #[must_use]
45    pub const fn verification_failure_ns(self) -> MeasurementSummary {
46        self.verification_failure_ns
47    }
48    /// Read successful local upload-metadata preparation durations in nanoseconds.
49    #[must_use]
50    pub const fn upload_metadata_success_ns(self) -> MeasurementSummary {
51        self.upload_metadata_success_ns
52    }
53    /// Read rejected local upload-metadata preparation durations in nanoseconds.
54    #[must_use]
55    pub const fn upload_metadata_failure_ns(self) -> MeasurementSummary {
56        self.upload_metadata_failure_ns
57    }
58    /// Read successful local upload-data preparation durations in nanoseconds.
59    #[must_use]
60    pub const fn upload_data_success_ns(self) -> MeasurementSummary {
61        self.upload_data_success_ns
62    }
63    /// Read rejected local upload-data preparation durations in nanoseconds.
64    #[must_use]
65    pub const fn upload_data_failure_ns(self) -> MeasurementSummary {
66        self.upload_data_failure_ns
67    }
68    /// Read successful prepared chunk sizes in bytes, including zero and repeated work.
69    #[must_use]
70    pub const fn prepared_chunk_bytes(self) -> MeasurementSummary {
71        self.prepared_chunk_bytes
72    }
73
74    fn record(
75        &mut self,
76        operation: LocalOperation,
77        elapsed: Duration,
78        succeeded: bool,
79        chunk_bytes: Option<usize>,
80    ) {
81        let duration = u64::try_from(elapsed.as_nanos()).unwrap_or(u64::MAX);
82        let summary = match (operation, succeeded) {
83            (LocalOperation::Verification, true) => &mut self.verification_success_ns,
84            (LocalOperation::Verification, false) => &mut self.verification_failure_ns,
85            (LocalOperation::UploadMetadata, true) => &mut self.upload_metadata_success_ns,
86            (LocalOperation::UploadMetadata, false) => &mut self.upload_metadata_failure_ns,
87            (LocalOperation::UploadData, true) => &mut self.upload_data_success_ns,
88            (LocalOperation::UploadData, false) => &mut self.upload_data_failure_ns,
89        };
90        summary.record(duration);
91        if let (LocalOperation::UploadData, true, Some(bytes)) = (operation, succeeded, chunk_bytes)
92        {
93            self.prepared_chunk_bytes
94                .record(u64::try_from(bytes).unwrap_or(u64::MAX));
95        }
96    }
97}
98
99#[derive(Clone, Copy)]
100pub(super) enum LocalOperation {
101    Verification,
102    UploadMetadata,
103    UploadData,
104}
105
106impl DownloadJournalGuard<'_> {
107    /// Read a copied local diagnostic snapshot without filesystem IO or fresh checks.
108    ///
109    /// Sampling is per guard lifetime, including rejected calls; ordinary journal
110    /// replay never supplies samples. Poison recovery is diagnostic only and cannot
111    /// change an operation result. No labels, IDs, byte contents or global registry
112    /// are retained. Host monotonic durations measure inclusive local work, not IC
113    /// instructions/cycles, unique transfer progress or an authoritative receipt.
114    #[must_use]
115    pub fn ic_snapshot_metrics(&self) -> IcSnapshotLocalMetrics {
116        *self
117            .ic_snapshot_metrics
118            .lock()
119            .unwrap_or_else(PoisonError::into_inner)
120    }
121
122    pub(super) fn record_ic_snapshot_metrics(
123        &self,
124        operation: LocalOperation,
125        started: Instant,
126        succeeded: bool,
127        chunk_bytes: Option<usize>,
128    ) {
129        let elapsed = started.elapsed();
130        self.ic_snapshot_metrics
131            .lock()
132            .unwrap_or_else(PoisonError::into_inner)
133            .record(operation, elapsed, succeeded, chunk_bytes);
134    }
135}
136
137#[cfg(test)]
138mod tests;