use parking_lot::Mutex;
use tracing::warn;
pub use lww_register::clock::{
assert_conformance, AdmittedTime, Clock, ClockDrift, Hlc, LogicalCounter, NodeId, PhysicalTime,
Timestamp, MAX_CLOCK_DRIFT,
};
use chrono::{DateTime, Utc};
pub(crate) fn phys_now() -> PhysicalTime {
PhysicalTime::from_millis(Utc::now().timestamp_millis().max(0) as u64)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum StampBound {
Verbatim,
Capped,
Unrepresentable,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct BoundedInstant {
instant: DateTime<Utc>,
bound: StampBound,
}
impl BoundedInstant {
pub(crate) fn from_stored_stamp(
stamp_physical: PhysicalTime,
budget: ClockDrift,
) -> BoundedInstant {
let admitted = AdmittedTime::clamped_to_drift(stamp_physical, phys_now(), budget);
match i64::try_from(admitted.physical().millis())
.ok()
.and_then(DateTime::from_timestamp_millis)
{
Some(instant) => BoundedInstant {
instant,
bound: if admitted.was_clamped() {
StampBound::Capped
} else {
StampBound::Verbatim
},
},
None => BoundedInstant {
instant: Utc::now(),
bound: StampBound::Unrepresentable,
},
}
}
pub(crate) fn instant(self) -> DateTime<Utc> {
self.instant
}
pub(crate) fn bound(self) -> StampBound {
self.bound
}
}
#[derive(Debug)]
pub(crate) struct HlcClock {
node_id: NodeId,
max_clock_drift: ClockDrift,
last: Mutex<Hlc>,
}
impl HlcClock {
pub fn new(node_id: NodeId) -> HlcClock {
HlcClock {
node_id,
max_clock_drift: MAX_CLOCK_DRIFT,
last: Mutex::new(Hlc::START),
}
}
#[allow(dead_code)]
pub fn with_max_clock_drift(mut self, max_clock_drift: ClockDrift) -> HlcClock {
self.max_clock_drift = max_clock_drift;
self
}
}
impl Clock for HlcClock {
fn node_id(&self) -> NodeId {
self.node_id
}
fn now(&self) -> Timestamp {
let pt = phys_now();
let mut last = self.last.lock();
let next = if pt > last.physical() {
Hlc::new(pt, LogicalCounter::ZERO)
} else {
last.next_tick()
};
*last = next;
Timestamp::new(next, self.node_id)
}
fn observe(&self, remote: Timestamp) {
let pt = phys_now();
let mut last = self.last.lock();
let admitted = AdmittedTime::clamped_to_drift(remote.physical(), pt, self.max_clock_drift);
if admitted.was_clamped() {
warn!(
remote_physical_ms = remote.physical().millis(),
remote_node_id = remote.node_id().get(),
phys_now_ms = pt.millis(),
cap_ms = admitted.physical().millis(),
max_clock_drift_ms = self.max_clock_drift.millis(),
"remote timestamp leads local clock by more than the configured max drift; \
clamping to cap to protect local clock state"
);
}
last.advance_past_remote(pt, admitted, remote.logical());
}
fn observe_trusted(&self, remote: Timestamp) {
let pt = phys_now();
let mut last = self.last.lock();
last.advance_past_remote(
pt,
AdmittedTime::trusted(remote.physical()),
remote.logical(),
);
}
}
#[cfg(test)]
#[derive(Debug)]
pub(crate) struct ManualClock {
node_id: NodeId,
last: Mutex<Hlc>,
}
#[cfg(test)]
impl ManualClock {
pub(crate) fn new(node_id: NodeId) -> ManualClock {
ManualClock {
node_id,
last: Mutex::new(Hlc::START),
}
}
}
#[cfg(test)]
impl Clock for ManualClock {
fn node_id(&self) -> NodeId {
self.node_id
}
fn now(&self) -> Timestamp {
let mut last = self.last.lock();
let next = last.next_tick();
*last = next;
Timestamp::new(next, self.node_id)
}
fn observe(&self, remote: Timestamp) {
let mut last = self.last.lock();
if remote.hlc() > *last {
*last = remote.hlc();
}
}
fn observe_trusted(&self, remote: Timestamp) {
self.observe(remote);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ts(physical: u64, logical: u32, node_id: u64) -> Timestamp {
Timestamp::new(
Hlc::new(
PhysicalTime::from_millis(physical),
LogicalCounter::new(logical),
),
NodeId::new(node_id),
)
}
#[test]
fn now_is_strictly_monotonic() {
let clock = HlcClock::new(NodeId::new(1));
let mut prev = clock.now();
for _ in 0..10_000 {
let next = clock.now();
assert!(next > prev, "{next:?} !> {prev:?}");
prev = next;
}
}
#[test]
fn logical_increments_when_physical_does_not_advance() {
let clock = HlcClock::new(NodeId::new(1));
let near_future = phys_now().saturating_add(ClockDrift::from_millis(60_000)); clock.observe(Timestamp::new(
Hlc::new(near_future, LogicalCounter::ZERO),
NodeId::new(9),
));
let a = clock.now();
let b = clock.now();
assert_eq!(a.physical(), b.physical());
assert_eq!(b, Timestamp::new(a.hlc().next_tick(), a.node_id()));
}
#[test]
fn observe_advances_past_a_future_timestamp() {
let clock = HlcClock::new(NodeId::new(1));
let future = Timestamp::new(
Hlc::new(
phys_now().saturating_add(ClockDrift::from_millis(5_000)),
LogicalCounter::new(5),
),
NodeId::new(2),
);
clock.observe(future);
let local = clock.now();
assert!(
local > future,
"local write {local:?} was not ordered after observed future timestamp {future:?}"
);
}
#[test]
fn manual_clock_is_deterministic() {
let clock = ManualClock::new(NodeId::new(7));
assert_eq!(clock.now(), ts(0, 1, 7));
assert_eq!(clock.now(), ts(0, 2, 7));
let remote = ts(50, 4, 9);
clock.observe(remote);
let local = clock.now();
assert_eq!(local, ts(50, 5, 7));
assert!(local > remote);
}
#[test]
fn observe_far_future_is_clamped() {
let clock = HlcClock::new(NodeId::new(1));
let before_clamp = clock.now();
let adversarial = ts(u64::MAX - 1, 0, 99);
clock.observe(adversarial);
let after_clamp = clock.now();
assert!(
after_clamp > before_clamp,
"monotonicity violated: {after_clamp:?} !> {before_clamp:?}"
);
let upper_bound = phys_now()
.saturating_add(MAX_CLOCK_DRIFT)
.saturating_add(ClockDrift::from_millis(10));
assert!(
after_clamp.physical() <= upper_bound,
"clock was not clamped: physical {:?} >> cap {:?}",
after_clamp.physical(),
upper_bound
);
}
#[test]
fn repeated_far_future_observes_do_not_escape_cap() {
let clock = HlcClock::new(NodeId::new(2));
for delta in [u64::MAX / 2, u64::MAX - 500, u64::MAX - 1] {
clock.observe(ts(delta, 0, 99));
}
let minted = clock.now();
let upper_bound = phys_now()
.saturating_add(MAX_CLOCK_DRIFT)
.saturating_add(ClockDrift::from_millis(10));
assert!(
minted.physical() <= upper_bound,
"physical {:?} escaped the cap {:?}",
minted.physical(),
upper_bound
);
}
#[test]
fn counter_overflow_rolls_physical_forward() {
let clock = HlcClock::new(NodeId::new(3));
let pinned_physical = phys_now().next_ms();
let max_counter_stamp = Timestamp::new(
Hlc::new(pinned_physical, LogicalCounter::new(u32::MAX)),
NodeId::new(99),
);
clock.observe(max_counter_stamp);
let rolled = clock.now();
assert!(
rolled > max_counter_stamp,
"timestamp not strictly greater after counter roll: {rolled:?} vs {max_counter_stamp:?}"
);
assert!(
rolled.physical() > pinned_physical,
"physical time did not roll forward: {rolled:?}"
);
}
#[test]
fn observe_trusted_bypasses_far_future_clamp() {
let far_future = Timestamp::new(
Hlc::new(
phys_now()
.saturating_add(MAX_CLOCK_DRIFT)
.saturating_add(ClockDrift::from_millis(5_000_000)),
LogicalCounter::new(3),
),
NodeId::new(7),
);
let trusted_clock = HlcClock::new(NodeId::new(1));
trusted_clock.observe_trusted(far_future);
let after_trusted = trusted_clock.now();
assert!(
after_trusted > far_future,
"observe_trusted did not advance the clock past the far-future stamp: \
next now() {after_trusted:?} is not > {far_future:?}"
);
let clamped_clock = HlcClock::new(NodeId::new(2));
clamped_clock.observe(far_future);
let after_clamped = clamped_clock.now();
let cap_upper = phys_now()
.saturating_add(MAX_CLOCK_DRIFT)
.saturating_add(ClockDrift::from_millis(10));
assert!(
after_clamped.physical() <= cap_upper,
"observe (clamped) let physical time escape the cap: {:?} > {:?}",
after_clamped.physical(),
cap_upper
);
assert!(
after_clamped < far_future,
"clamped observe produced a stamp >= the far-future value: \
{after_clamped:?} should be < {far_future:?}"
);
}
#[test]
fn bounded_instant_covers_every_stamp_regime() {
let budget = MAX_CLOCK_DRIFT;
let normal = phys_now().millis() - 1_000;
let b = BoundedInstant::from_stored_stamp(PhysicalTime::from_millis(normal), budget);
assert_eq!(b.bound(), StampBound::Verbatim);
assert_eq!(b.instant().timestamp_millis(), normal as i64);
let far = phys_now().millis() + 10_000 * 365 * 24 * 3_600_000; let b = BoundedInstant::from_stored_stamp(PhysicalTime::from_millis(far), budget);
assert_eq!(b.bound(), StampBound::Capped);
let cap_upper = phys_now().saturating_add(budget).millis() as i64;
assert!(
b.instant().timestamp_millis() <= cap_upper,
"instant {} escaped the cap {cap_upper}",
b.instant()
);
assert!(
b.instant().timestamp_millis() >= phys_now().millis() as i64,
"a capped instant must still be in the future, not in the past"
);
let b = BoundedInstant::from_stored_stamp(PhysicalTime::from_millis(u64::MAX), budget);
assert_eq!(b.bound(), StampBound::Capped);
let cap_upper = phys_now().saturating_add(budget).millis() as i64;
assert!(
b.instant().timestamp_millis() > 0,
"wrapped to a pre-epoch instant: {}",
b.instant()
);
assert!(
b.instant().timestamp_millis() <= cap_upper,
"instant {} escaped the cap {cap_upper}",
b.instant()
);
let b = BoundedInstant::from_stored_stamp(
PhysicalTime::from_millis(u64::MAX),
ClockDrift::from_millis(u64::MAX),
);
assert_eq!(b.bound(), StampBound::Unrepresentable);
let skew = (b.instant().timestamp_millis() - phys_now().millis() as i64).abs();
assert!(
skew < 60_000,
"fallback instant is not ~now: {}",
b.instant()
);
}
#[test]
fn custom_max_clock_drift_is_respected() {
let drift = ClockDrift::from_millis(1_000); let clock = HlcClock::new(NodeId::new(1)).with_max_clock_drift(drift);
let remote_physical = phys_now().saturating_add(ClockDrift::from_millis(60_000));
clock.observe(Timestamp::new(
Hlc::new(remote_physical, LogicalCounter::ZERO),
NodeId::new(99),
));
let minted = clock.now();
let upper_bound = phys_now()
.saturating_add(drift)
.saturating_add(ClockDrift::from_millis(10));
assert!(
minted.physical() <= upper_bound,
"custom drift cap not enforced: physical {:?} > cap {:?}",
minted.physical(),
upper_bound
);
}
#[test]
fn hlc_clock_is_conformant() {
assert_conformance(&HlcClock::new(NodeId::new(1)));
}
#[test]
fn manual_clock_is_conformant() {
assert_conformance(&ManualClock::new(NodeId::new(1)));
}
}