Skip to main content

query_runtime/observability/
clock.rs

1//! Clock abstraction for query-relative runtime observations.
2
3use std::{
4    sync::atomic::{AtomicU64, Ordering},
5    time::{Instant, SystemTime, UNIX_EPOCH},
6};
7
8/// Clock sampled by a runtime observation session while holding recorder order.
9pub trait RuntimeObservationClock: Send + Sync + 'static {
10    /// Query-relative monotonic nanoseconds.
11    fn monotonic_ns(&self) -> u64;
12
13    /// Optional Unix epoch anchor captured once for cross-artifact alignment.
14    fn unix_epoch_ns(&self) -> Option<u64>;
15}
16
17/// System monotonic clock with one wall-clock anchor.
18#[derive(Debug)]
19pub(crate) struct SystemRuntimeObservationClock {
20    origin: Instant,
21    unix_epoch_ns: Option<u64>,
22}
23
24impl SystemRuntimeObservationClock {
25    /// Captures a new monotonic origin and optional Unix epoch anchor.
26    pub fn new() -> Self {
27        let unix_epoch_ns = SystemTime::now()
28            .duration_since(UNIX_EPOCH)
29            .ok()
30            .and_then(|duration| u64::try_from(duration.as_nanos()).ok());
31        Self { origin: Instant::now(), unix_epoch_ns }
32    }
33}
34
35impl Default for SystemRuntimeObservationClock {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl RuntimeObservationClock for SystemRuntimeObservationClock {
42    fn monotonic_ns(&self) -> u64 {
43        u64::try_from(self.origin.elapsed().as_nanos()).unwrap_or(u64::MAX)
44    }
45
46    fn unix_epoch_ns(&self) -> Option<u64> {
47        self.unix_epoch_ns
48    }
49}
50
51/// Deterministic clock for contract tests and controlled simulations.
52#[derive(Debug)]
53pub struct DeterministicRuntimeObservationClock {
54    monotonic_ns: AtomicU64,
55    unix_epoch_ns: Option<u64>,
56}
57
58impl DeterministicRuntimeObservationClock {
59    /// Creates a deterministic clock at zero query-relative nanoseconds.
60    pub const fn new(unix_epoch_ns: Option<u64>) -> Self {
61        Self { monotonic_ns: AtomicU64::new(0), unix_epoch_ns }
62    }
63
64    /// Sets the next sampled query-relative value.
65    pub fn set_monotonic_ns(&self, monotonic_ns: u64) {
66        self.monotonic_ns.store(monotonic_ns, Ordering::Release);
67    }
68
69    /// Advances query-relative time and returns the new value.
70    pub fn advance(&self, nanoseconds: u64) -> u64 {
71        self.monotonic_ns.fetch_add(nanoseconds, Ordering::AcqRel).saturating_add(nanoseconds)
72    }
73}
74
75impl RuntimeObservationClock for DeterministicRuntimeObservationClock {
76    fn monotonic_ns(&self) -> u64 {
77        self.monotonic_ns.load(Ordering::Acquire)
78    }
79
80    fn unix_epoch_ns(&self) -> Option<u64> {
81        self.unix_epoch_ns
82    }
83}