pub struct HlcClock<C = SystemClock> { /* private fields */ }Expand description
A hybrid logical clock for one node.
The clock holds a wall time and a logical counter, and it follows the
algorithm in Kulkarni et al., Logical Physical Clocks and Consistent
Snapshots in Globally Distributed Databases (OPODIS 2014). HlcClock::now
is the paper’s send event and HlcClock::observe is its receive event.
The pair keeps two guarantees. The stamps one node mints strictly increase.
A stamp that a node received is always below every stamp that the node
mints after it, so the order the stamps give agrees with cause and effect.
Every method takes &self. The state sits behind a Mutex, and the
critical section only compares and assigns integers, so a producer task, a
consumer task, and an observability agent can share one clock.
The physical clock is a type parameter, so a test drives the whole state
machine with a ManualClock and no sleep.
Implementations§
Source§impl HlcClock<SystemClock>
impl HlcClock<SystemClock>
Sourcepub fn new(node: NodeId) -> Self
pub fn new(node: NodeId) -> Self
A clock for node that reads the host’s wall clock and fences at
DEFAULT_MAX_OFFSET.
Source§impl<C: PhysicalClock> HlcClock<C>
impl<C: PhysicalClock> HlcClock<C>
Sourcepub fn with_clock(node: NodeId, physical: C) -> Self
pub fn with_clock(node: NodeId, physical: C) -> Self
A clock for node that reads physical and fences at
DEFAULT_MAX_OFFSET.
Sourcepub fn with_max_offset(node: NodeId, physical: C, max_offset: Time) -> Self
pub fn with_max_offset(node: NodeId, physical: C, max_offset: Time) -> Self
A clock for node that reads physical and fences at max_offset.
The clock starts at the current reading of physical, with the counter
at zero. A negative max_offset has the same effect as zero: the clock
then refuses every stamp whose wall time is above its physical clock.
Sourcepub fn max_offset(&self) -> Time
pub fn max_offset(&self) -> Time
The maximum offset the self-fence allows.
Sourcepub fn now(&self) -> Result<Hlc, HlcError>
pub fn now(&self) -> Result<Hlc, HlcError>
The next stamp for a local event, such as a record this node produces.
This is the paper’s send event. The new wall time is the later of the current logical wall time and the physical clock. The counter goes up by one when the wall time did not move, and back to zero when it did. Two calls never return the same stamp, whatever the physical clock does.
§Errors
Returns HlcError::CounterOverflow when the counter reaches
u32::MAX inside one microsecond. That needs more than four billion
stamps in one microsecond, and the clock reports it rather than let the
counter wrap.
Sourcepub fn observe(&self, peer: Hlc) -> Result<Hlc, HlcError>
pub fn observe(&self, peer: Hlc) -> Result<Hlc, HlcError>
Merges a peer’s stamp into this clock, and returns the stamp for the receive event.
This is the paper’s receive event. The new wall time is the latest of the local logical wall time, the peer’s wall time, and the physical clock. The counter then follows the source of that wall time: it is one above the larger of the two counters when the local clock and the peer agree, one above the counter of whichever side supplied the wall time when only one side did, and zero when the physical clock overtook both.
The returned stamp is always above peer, so the record this node
writes next sorts after the record it read.
The clock also fences itself. It refuses a stamp whose wall time is more
than HlcClock::max_offset ahead of the local physical clock. It
returns HlcError::PeerTooFarAhead, and it does not move. A hybrid
logical clock adopts the highest wall time it sees, so one host with a
clock set to the year 2098 would drag every node that reads its records
into 2098, and then every node that reads those nodes’ records. Nothing
brings the fleet back, because logical time never goes backwards. The
fence keeps the damage inside the broken host.
A stamp behind the local physical clock is always safe, and the clock always accepts it. The fence is one-sided for that reason.
§Errors
Returns HlcError::PeerTooFarAhead when the self-fence refuses the
stamp, and HlcError::CounterOverflow when the merged counter
reaches u32::MAX inside one microsecond.
Sourcepub fn observation(&self) -> ClockObservation
pub fn observation(&self) -> ClockObservation
What this clock has learned about clock skew.
The snapshot is consistent: it reads the physical clock and the whole state together.