Skip to main content

lash_core/runtime/
clock.rs

1//! Injected time source.
2//!
3//! lash is a replayable durable runtime, so time is an effect like any other:
4//! durable timestamps must be reproducible under replay, and timeout/backoff
5//! logic must be drivable deterministically in tests. Every wall-clock and
6//! monotonic read in the runtime path goes through a [`Clock`]; the default
7//! [`SystemClock`] reads the real OS clock, and tests inject a controllable
8//! one.
9//!
10//! Boundary: `now()` is monotonic (measurement/elapsed only, never persisted);
11//! `timestamp_ms`/`timestamp_rfc3339` are wall-clock (durable records). `sleep`
12//! and `sleep_until` replace direct `tokio::time` calls so a fake clock can
13//! resolve them without real wall-clock waits.
14
15use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
16
17use async_trait::async_trait;
18
19/// Runtime and embedded-store time source. Cloneable as `Arc<dyn Clock>`;
20/// carried on [`RuntimeHostConfig`](super::RuntimeHostConfig).
21///
22/// SQLite and in-memory persistence read this host-injectable clock because
23/// they run in the same clock domain as their host. PostgreSQL lease decisions
24/// deliberately remain database-authoritative (`transaction_timestamp()` /
25/// `clock_timestamp()`) to protect fencing across skewed hosts; the
26/// `postgres_clock_contract` tests pin that boundary.
27#[async_trait]
28pub trait Clock: Send + Sync + std::fmt::Debug {
29    /// Monotonic instant for measuring elapsed time. Never persisted.
30    fn now(&self) -> Instant;
31
32    /// Wall-clock time as epoch milliseconds, for durable records.
33    fn timestamp_ms(&self) -> u64;
34
35    /// Wall-clock time as an RFC 3339 string, for durable records.
36    fn timestamp_rfc3339(&self) -> String;
37
38    /// Wall-clock time as a UTC datetime, for trace records.
39    fn timestamp_datetime(&self) -> chrono::DateTime<chrono::Utc>;
40
41    /// Sleep for `duration`. Replaces `tokio::time::sleep`.
42    async fn sleep(&self, duration: Duration);
43
44    /// Sleep until `deadline` (a value from [`now`](Clock::now)). Replaces
45    /// `tokio::time::sleep_until`.
46    async fn sleep_until(&self, deadline: Instant);
47}
48
49/// The real OS clock. Native behavior is identical to the direct `std`/`tokio`
50/// calls it replaces.
51#[derive(Debug, Default, Clone, Copy)]
52pub struct SystemClock;
53
54#[async_trait]
55impl Clock for SystemClock {
56    fn now(&self) -> Instant {
57        Instant::now()
58    }
59
60    fn timestamp_ms(&self) -> u64 {
61        SystemTime::now()
62            .duration_since(UNIX_EPOCH)
63            .unwrap_or_default()
64            .as_millis() as u64
65    }
66
67    fn timestamp_rfc3339(&self) -> String {
68        self.timestamp_datetime().to_rfc3339()
69    }
70
71    fn timestamp_datetime(&self) -> chrono::DateTime<chrono::Utc> {
72        chrono::Utc::now()
73    }
74
75    async fn sleep(&self, duration: Duration) {
76        tokio::time::sleep(duration).await;
77    }
78
79    async fn sleep_until(&self, deadline: Instant) {
80        tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)).await;
81    }
82}