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