Skip to main content

hashiverse_lib/tools/time_provider/
moka_clock.rs

1//! Adapter exposing a [`TimeProvider`] as a [`moka::ExternalClock`].
2//!
3//! By default a moka cache measures its TTL/TTI/expiry against wall-clock
4//! `std::time::Instant`, which ignores our pluggable `TimeProvider` — so under a
5//! `ScaledTimeProvider` (integration tests run time fast) cache eviction would
6//! fire on real time, out of step with the rest of the system. Building a cache
7//! with `.external_clock(Arc::new(TimeProviderMokaClock::new(time_provider)))`
8//! routes all of moka's time decisions through that provider instead, so the
9//! same code behaves correctly under both real and scaled clocks.
10//!
11//! Relies on the `ExternalClock` seam added to our vendored moka fork
12//! (see `3rdparty/moka/PATCH_NOTES.md`).
13
14use std::sync::Arc;
15use std::time::Duration;
16
17use crate::tools::time_provider::time_provider::TimeProvider;
18
19/// Bridges a [`TimeProvider`] to [`moka::ExternalClock`]. moka tracks time as a
20/// `Duration` elapsed since the clock's origin, so we capture the provider's
21/// reading at construction and report the difference on each call.
22pub struct TimeProviderMokaClock {
23    time_provider: Arc<dyn TimeProvider>,
24    origin_millis: i64,
25}
26
27impl TimeProviderMokaClock {
28    pub fn new(time_provider: Arc<dyn TimeProvider>) -> Self {
29        let origin_millis = time_provider.current_time_millis().0;
30        Self { time_provider, origin_millis }
31    }
32}
33
34impl moka::ExternalClock for TimeProviderMokaClock {
35    fn elapsed_since_origin(&self) -> Duration {
36        let now = self.time_provider.current_time_millis().0;
37        Duration::from_millis(now.saturating_sub(self.origin_millis).max(0) as u64)
38    }
39}