Expand description
The one clock port: monotonic time, sleeping, and the wall time since the epoch.
§One trait rather than one per module
The control plane and the session each had a Clock of their own, over different time
sources: the control plane’s read std::time::Instant and the session’s read tokio’s.
Under a deterministic simulator those disagree. tokio’s clock is virtual there and std’s
isn’t, so a control-plane wait whose sleeps advanced virtual time measured its deadline
on the host’s real time and never reached it. Sandbox already waits on the plane’s clock
rather than one of its own, so a deadline isn’t measured on one clock while the polls sleep
on another. One trait, with tokio’s clock in production, keeps that property for everything
that reads time. The trait is re-exported at control::Clock and at session::Clock, the
paths each half had.
§Why a trait rather than tokio::time::pause
The waits are driven by elapsed comparisons against a deadline and a stall grace, and a
test has to be able to say “now 300 seconds have passed” between two polls of a fake
control plane. A paused tokio clock can do that, but only for code that sleeps on tokio,
and it makes every test in a file share one global clock state. A Clock parameter
makes the dependency visible in the signature.
§Monotonic and wall time are separate readings
Clock::elapsed is monotonic, the same reasoning the Python client’s injectable
time.monotonic carried: a wait’s deadline and a proxy token’s age are durations, and a
wall clock that steps backward would hold a passed deadline open. Clock::unix_now is the
wall reading, for the values that are dates rather than durations: a name record’s
timestamp, an exec id, the cost engine’s today. Deriving one from the other would make a
stepped wall clock move a deadline.
Traits§
- Clock
- Time, injectable so a wait or a refresh boundary is a test rather than a wait.