Skip to main content

ic_timers/snapshot/
mod.rs

1//! Provider-neutral identity and coherent canonical snapshot values.
2//!
3//! Public snapshots are inert observations with private top-level fields. The
4//! registry is their only constructor and mutation authority.
5
6mod identity;
7mod metrics;
8mod model;
9
10pub use identity::{
11    MAX_TIMER_LABEL_BYTES, TimerIdentity, TimerIdentityError, TimerIdentityField, TimerLabel,
12    TimerLabelError,
13};
14pub use metrics::{
15    MeasurementSummary, TimerCounters, TimerObservabilitySnapshot, TimerPerformance,
16};
17pub use model::{
18    DeclarationLifetime, InactiveReason, OrdinaryRuntimeStateSnapshot, TimerCompletion,
19    TimerCompletionOutcome, TimerControlFailure, TimerDirectiveSnapshot, TimerEpoch,
20    TimerLastOutcome, TimerOutcomeSnapshot, TimerPolicy, TimerProcessCondition,
21    TimerRegistrationStatus, TimerRunResult, TimerRuntimeStateSnapshot, TimerSchedulingMode,
22    WatchdogAttemptSnapshot, WatchdogAttemptStatus, WatchdogDecision, WatchdogRunResult,
23    WatchdogRuntimeStateSnapshot,
24};
25
26/// Canonical provider-neutral operator snapshot for one logical timer.
27#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct TimerSnapshot {
29    identity: TimerIdentity,
30    policy: TimerPolicy,
31    lifetime: DeclarationLifetime,
32    state: TimerRuntimeStateSnapshot,
33    scheduling_mode: TimerSchedulingMode,
34    latest_directive: Option<TimerDirectiveSnapshot>,
35    latest_requested_delay_ns: Option<u64>,
36    latest_armed_delay_ns: Option<u64>,
37    observability: TimerObservabilitySnapshot,
38}
39
40impl TimerSnapshot {
41    #[allow(clippy::too_many_arguments)]
42    pub(crate) const fn new(
43        identity: TimerIdentity,
44        policy: TimerPolicy,
45        lifetime: DeclarationLifetime,
46        state: TimerRuntimeStateSnapshot,
47        scheduling_mode: TimerSchedulingMode,
48        latest_directive: Option<TimerDirectiveSnapshot>,
49        latest_requested_delay_ns: Option<u64>,
50        latest_armed_delay_ns: Option<u64>,
51        observability: &TimerObservabilitySnapshot,
52    ) -> Self {
53        Self {
54            identity,
55            policy,
56            lifetime,
57            state,
58            scheduling_mode,
59            latest_directive,
60            latest_requested_delay_ns,
61            latest_armed_delay_ns,
62            observability: *observability,
63        }
64    }
65
66    /// Return the stable structured identity.
67    #[must_use]
68    pub const fn identity(&self) -> &TimerIdentity {
69        &self.identity
70    }
71
72    /// Return the configured scheduling policy.
73    #[must_use]
74    pub const fn policy(&self) -> TimerPolicy {
75        self.policy
76    }
77
78    /// Return whether the declaration remains after terminal stop.
79    #[must_use]
80    pub const fn lifetime(&self) -> DeclarationLifetime {
81        self.lifetime
82    }
83
84    /// Return the closed policy-specific runtime state.
85    #[must_use]
86    pub const fn state(&self) -> TimerRuntimeStateSnapshot {
87        self.state
88    }
89
90    /// Return the effective scheduling mode.
91    ///
92    /// A new declaration starts with its configured policy mode. Later
93    /// requests and completed directives update this value, including after
94    /// the declaration becomes inactive.
95    #[must_use]
96    pub const fn scheduling_mode(&self) -> TimerSchedulingMode {
97        self.scheduling_mode
98    }
99
100    /// Return the latest completed ordinary directive.
101    #[must_use]
102    pub const fn latest_directive(&self) -> Option<TimerDirectiveSnapshot> {
103        self.latest_directive
104    }
105
106    /// Return the latest requested relative delay.
107    #[must_use]
108    pub const fn latest_requested_delay_ns(&self) -> Option<u64> {
109        self.latest_requested_delay_ns
110    }
111
112    /// Return the latest relative delay whose provider arm committed.
113    #[must_use]
114    pub const fn latest_armed_delay_ns(&self) -> Option<u64> {
115        self.latest_armed_delay_ns
116    }
117
118    /// Return the next authoritative absolute deadline.
119    #[must_use]
120    pub const fn next_deadline_ns(&self) -> Option<u64> {
121        self.state.next_deadline_ns()
122    }
123
124    /// Return a portable registration projection.
125    #[must_use]
126    pub fn registration_status(&self) -> TimerRegistrationStatus {
127        self.state.into()
128    }
129
130    /// Return an operator-facing condition derived from coherent state.
131    #[must_use]
132    pub const fn process_condition(&self) -> TimerProcessCondition {
133        match self.state {
134            TimerRuntimeStateSnapshot::Inactive {
135                reason: InactiveReason::Cancelled,
136            } => TimerProcessCondition::Disabled,
137            TimerRuntimeStateSnapshot::Inactive {
138                reason: InactiveReason::InvariantFailure | InactiveReason::ControlFailure(_),
139            } => TimerProcessCondition::Failed,
140            TimerRuntimeStateSnapshot::Inactive {
141                reason: InactiveReason::Stopped,
142            } if matches!(
143                self.observability.outcomes().last_outcome(),
144                Some(TimerLastOutcome::Completed(
145                    TimerCompletionOutcome::RetryableFailure
146                ))
147            ) =>
148            {
149                TimerProcessCondition::Failed
150            }
151            TimerRuntimeStateSnapshot::Inactive { .. } => TimerProcessCondition::Idle,
152            TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_)
153                if matches!(self.scheduling_mode, TimerSchedulingMode::Retry) =>
154            {
155                TimerProcessCondition::Retrying
156            }
157            TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_) => {
158                TimerProcessCondition::Active
159            }
160        }
161    }
162
163    /// Return the latest authoritative callback generation.
164    #[must_use]
165    pub const fn generation(&self) -> Option<u64> {
166        match self.state {
167            TimerRuntimeStateSnapshot::Inactive { .. } => None,
168            TimerRuntimeStateSnapshot::Ordinary(
169                OrdinaryRuntimeStateSnapshot::Scheduled { generation, .. }
170                | OrdinaryRuntimeStateSnapshot::Running { generation },
171            ) => Some(generation),
172            TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::Scheduled {
173                scheduler_generation,
174                ..
175            }) => Some(scheduler_generation),
176            TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::AwaitingWork {
177                successor_generation,
178                ..
179            }) => Some(successor_generation),
180        }
181    }
182
183    /// Return epoch-scoped outcomes, counters, and measurements.
184    #[must_use]
185    pub const fn observability(&self) -> TimerObservabilitySnapshot {
186        self.observability
187    }
188
189    /// Return recovery-sensitive expected-failure state directly.
190    #[must_use]
191    pub const fn consecutive_expected_failures(&self) -> u64 {
192        self.observability.consecutive_expected_failures()
193    }
194}
195
196#[cfg(test)]
197mod tests;