Skip to main content

rust_supervisor/event/
time.rs

1//! Event timing primitives for lifecycle diagnostics.
2//!
3//! This module owns sequence numbers, correlation identifiers, and event time
4//! capture. It does not depend on the runtime so tests can create deterministic
5//! event timestamps.
6
7use crate::id::types::{Attempt, Generation};
8use serde::{Deserialize, Serialize};
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::time::{Duration, SystemTime, UNIX_EPOCH};
11use uuid::Uuid;
12
13/// Monotonic event sequence allocated by an event source.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
15pub struct EventSequence {
16    /// One-based event sequence value.
17    pub value: u64,
18}
19
20impl EventSequence {
21    /// Creates an event sequence from a raw value.
22    ///
23    /// # Arguments
24    ///
25    /// - `value`: One-based sequence value assigned by the caller.
26    ///
27    /// # Returns
28    ///
29    /// Returns an [`EventSequence`] that preserves the provided value.
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// let sequence = rust_supervisor::event::time::EventSequence::new(7);
35    /// assert_eq!(sequence.value, 7);
36    /// ```
37    pub fn new(value: u64) -> Self {
38        Self { value }
39    }
40}
41
42/// Atomic allocator for monotonic event sequences.
43#[derive(Debug)]
44pub struct EventSequenceSource {
45    /// Last sequence value handed to a caller.
46    next_value: AtomicU64,
47}
48
49impl EventSequenceSource {
50    /// Creates a sequence source that starts at one.
51    ///
52    /// # Arguments
53    ///
54    /// This function has no arguments.
55    ///
56    /// # Returns
57    ///
58    /// Returns a new [`EventSequenceSource`].
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// let source = rust_supervisor::event::time::EventSequenceSource::new();
64    /// assert_eq!(source.next().value, 1);
65    /// assert_eq!(source.next().value, 2);
66    /// ```
67    pub fn new() -> Self {
68        Self {
69            next_value: AtomicU64::new(1),
70        }
71    }
72
73    /// Allocates the next sequence.
74    ///
75    /// # Arguments
76    ///
77    /// This function has no arguments.
78    ///
79    /// # Returns
80    ///
81    /// Returns the next [`EventSequence`].
82    pub fn next(&self) -> EventSequence {
83        EventSequence::new(self.next_value.fetch_add(1, Ordering::Relaxed))
84    }
85}
86
87impl Default for EventSequenceSource {
88    /// Creates the default event sequence source.
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94/// Identifier that connects related lifecycle facts.
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
96pub struct CorrelationId {
97    /// UUID used by commands, attempts, and derived observability signals.
98    pub value: Uuid,
99}
100
101impl CorrelationId {
102    /// Creates a random correlation identifier.
103    ///
104    /// # Arguments
105    ///
106    /// This function has no arguments.
107    ///
108    /// # Returns
109    ///
110    /// Returns a new [`CorrelationId`].
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// let id = rust_supervisor::event::time::CorrelationId::new();
116    /// assert!(!id.value.is_nil());
117    /// ```
118    pub fn new() -> Self {
119        Self {
120            value: Uuid::new_v4(),
121        }
122    }
123
124    /// Creates a correlation identifier from a UUID.
125    ///
126    /// # Arguments
127    ///
128    /// - `value`: UUID chosen by the caller.
129    ///
130    /// # Returns
131    ///
132    /// Returns a [`CorrelationId`] containing `value`.
133    pub fn from_uuid(value: Uuid) -> Self {
134        Self { value }
135    }
136}
137
138impl Default for CorrelationId {
139    /// Creates the default correlation identifier.
140    fn default() -> Self {
141        Self::new()
142    }
143}
144
145/// Time data attached to a lifecycle event.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147pub struct EventTime {
148    /// Wall-clock time as nanoseconds since the Unix epoch.
149    pub unix_nanos: u128,
150    /// Monotonic time source as nanoseconds supplied by the runtime.
151    pub monotonic_nanos: u128,
152    /// Supervisor uptime in milliseconds.
153    pub supervisor_uptime_ms: u64,
154    /// Child generation related to the event.
155    pub generation: Generation,
156    /// Child attempt related to the event.
157    pub attempt: Attempt,
158}
159
160impl EventTime {
161    /// Captures wall-clock timing and caller-supplied monotonic timing.
162    ///
163    /// # Arguments
164    ///
165    /// - `monotonic_nanos`: Runtime monotonic clock value in nanoseconds.
166    /// - `supervisor_uptime_ms`: Supervisor uptime in milliseconds.
167    /// - `generation`: Child generation for this lifecycle fact.
168    /// - `attempt`: Child attempt for this lifecycle fact.
169    ///
170    /// # Returns
171    ///
172    /// Returns an [`EventTime`] value.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// let time = rust_supervisor::event::time::EventTime::from_parts(
178    ///     10,
179    ///     2,
180    ///     rust_supervisor::id::types::Generation::initial(),
181    ///     rust_supervisor::id::types::Attempt::first(),
182    /// );
183    /// assert_eq!(time.monotonic_nanos, 10);
184    /// ```
185    pub fn from_parts(
186        monotonic_nanos: u128,
187        supervisor_uptime_ms: u64,
188        generation: Generation,
189        attempt: Attempt,
190    ) -> Self {
191        Self {
192            unix_nanos: system_time_nanos(SystemTime::now()),
193            monotonic_nanos,
194            supervisor_uptime_ms,
195            generation,
196            attempt,
197        }
198    }
199
200    /// Creates deterministic event time for tests and replay.
201    ///
202    /// # Arguments
203    ///
204    /// - `unix_nanos`: Wall-clock timestamp in nanoseconds.
205    /// - `monotonic_nanos`: Monotonic timestamp in nanoseconds.
206    /// - `supervisor_uptime_ms`: Supervisor uptime in milliseconds.
207    /// - `generation`: Child generation for this event.
208    /// - `attempt`: Child attempt for this event.
209    ///
210    /// # Returns
211    ///
212    /// Returns an [`EventTime`] value with exact caller-provided fields.
213    pub fn deterministic(
214        unix_nanos: u128,
215        monotonic_nanos: u128,
216        supervisor_uptime_ms: u64,
217        generation: Generation,
218        attempt: Attempt,
219    ) -> Self {
220        Self {
221            unix_nanos,
222            monotonic_nanos,
223            supervisor_uptime_ms,
224            generation,
225            attempt,
226        }
227    }
228}
229
230/// Wrapper that answers when a lifecycle fact happened.
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
232pub struct When {
233    /// Detailed timing data for the event.
234    pub time: EventTime,
235}
236
237impl When {
238    /// Creates a `When` value from event time.
239    ///
240    /// # Arguments
241    ///
242    /// - `time`: Event time captured by the caller.
243    ///
244    /// # Returns
245    ///
246    /// Returns a [`When`] wrapper.
247    ///
248    /// # Examples
249    ///
250    /// ```
251    /// let when = rust_supervisor::event::time::When::new(
252    ///     rust_supervisor::event::time::EventTime::deterministic(
253    ///         1,
254    ///         1,
255    ///         0,
256    ///         rust_supervisor::id::types::Generation::initial(),
257    ///         rust_supervisor::id::types::Attempt::first(),
258    ///     ),
259    /// );
260    /// assert_eq!(when.time.unix_nanos, 1);
261    /// ```
262    pub fn new(time: EventTime) -> Self {
263        Self { time }
264    }
265}
266
267/// Converts system time into nanoseconds with a zero fallback before epoch.
268///
269/// # Arguments
270///
271/// - `time`: Wall-clock value that should be converted.
272///
273/// # Returns
274///
275/// Returns nanoseconds since Unix epoch.
276fn system_time_nanos(time: SystemTime) -> u128 {
277    time.duration_since(UNIX_EPOCH)
278        .unwrap_or(Duration::ZERO)
279        .as_nanos()
280}