DBRaven

Summary

Two nodes' clocks disagree at a given moment (skew), and each node's own clock accumulates its own error over time (drift). Systems that assume synchronized, monotonically advancing wall-clock time break in specific, predictable ways when either assumption fails: wrong event ordering, early or late expiration, and leases held by two nodes at once.

Description

Drift and skew are related but distinct, and the distinction matters for which fix applies. Drift is a property of one clock: its hardware oscillator runs slightly fast or slow, so left uncorrected its reading diverges from true time at a roughly constant rate, commonly tens of milliseconds per day on commodity hardware. Skew is a property of two clocks compared to each other: the difference between what they currently read, which can come from different drift rates, different times since last sync, or a correction applied to one but not the other. NTP (ntpd, chronyd) corrects drift on each node against a reference; skew is what remains between nodes even with NTP running everywhere, because synchronization is not instantaneous or perfect across a fleet. Standard NTP achieves roughly plus-or-minus 10ms accuracy under good network conditions; that residual is the skew a distributed system must tolerate or design around.

How NTP corrects matters as much as whether it runs. NTP can correct in two ways: slewing gradually speeds up or slows down the local clock's rate until it converges, with no discontinuity. Stepping jumps the clock directly to the corrected time, which can move it backward if the local clock was ahead. A backward step is a distinct hazard from ordinary skew: it can violate monotonicity on a single node by itself, no second node involved, so a duration measured by reading wall-clock time twice can come out negative, and a piece of code computing "has 30 seconds passed" can be fooled twice by the same jump. Duration and timeout logic should read a monotonic clock source (POSIX CLOCK_MONOTONIC, not CLOCK_REALTIME), which is guaranteed to never move backward, precisely because it is not subject to NTP step corrections. Wall-clock time is still needed for anything that must be compared across nodes or persisted as a timestamp; monotonic time is for measuring elapsed duration on one node.

Timestamp-based ordering across nodes is a skew problem. If Node A processes event X at its local t=100ms and Node B processes event Y at its local t=99ms, and the two clocks differ by 15ms, the apparent ordering, Y before X, may be backward from the true order. Systems that order events by wall-clock timestamp without a logical clock are exposed to this whenever skew exceeds the gap between the events.

Cache expiration is a skew problem at the boundary. A TTL computed on Node A expires at absolute time T. Node B's clock, reading T+15ms or T-15ms, evicts up to 15ms early or late. For short TTLs (under a second) that error is a meaningful fraction of the TTL itself.

Distributed lock and lease timeout is where skew becomes a correctness problem, not just an approximation error. A lease granted at time T for 30 seconds expires at T+30s by the coordinator's clock. If the leaseholder's clock reads 5 seconds behind the coordinator's, the coordinator revokes the lease 5 seconds before the leaseholder believes it has expired, and a new grantee can start acting as leader while the old one still believes it holds the lease: split_brain at the lease level. This is a second, independent reason lease-based coordination is structurally unsafe, alongside the unbounded-process-pause argument in leader_election: fencing tokens are the fix for both, because they make the storage layer, not the clock, the source of truth for who is allowed to act.

JWT and session token expiry is a skew problem with a standard tolerance fix. Tokens carry an exp timestamp. If the validating server's clock runs ahead of the issuing server's, valid tokens appear expired before their real expiry. RFC 7519 recommends a few minutes of tolerance in validation for exactly this reason.

Google Spanner's TrueTime bounds skew instead of tolerating it. Spanner uses GPS receivers and atomic clocks per datacenter so every node's clock uncertainty is bounded by a known interval epsilon (typically a few milliseconds, historically up to about 7ms worst case). TrueTime returns not a single timestamp but an interval guaranteed to contain the true time. Spanner's commit wait then holds a transaction's effects invisible until the earliest bound of the current TrueTime interval has passed the transaction's assigned commit timestamp, guaranteeing any later-starting transaction really did start after the earlier one committed in real time. This is external consistency bought by bounding and waiting out skew, not by making it zero, and it depends on infrastructure (GPS and atomic clock references) not available outside a handful of operators.

Virtualized environments add their own drift and skew sources on top of ordinary NTP behavior: VM live migration, hypervisor clock skew, and a suspended VM resuming with a stale clock are common causes. Cloud providers typically offer a low-latency internal time-sync endpoint (for example, AWS's Amazon Time Sync Service) specifically because reaching an external NTP pool from inside a VM adds its own latency-driven skew.

Logical clocks sidestep the whole physical-clock problem for ordering. Lamport clocks and vector clocks order events by causal relationship rather than wall-clock reading, so they are immune to drift and skew by construction, at the cost of not being human-readable wall-clock time. Hybrid Logical Clocks combine a Lamport-style causal counter with a physical-time component bounded to stay close to real time, giving both causal ordering and a timestamp still useful for humans and TTL-style logic. CockroachDB uses HLCs for exactly this combination.

Characteristics

Propagationisolated
Time to detectActive monitoring of per-node clock offset (chronyc tracking, node_exporter's time_offset metric) detects skew continuously and directly. Application-level symptoms (lease conflicts, token rejection) surface within seconds to minutes once they produce errors, but tracing those symptoms back to clock skew as the root cause often takes longer without direct offset monitoring in place.
Blast radiusEffects are subtle and localized but real: wrong event ordering, security-relevant token rejection or acceptance, and log timestamps across nodes that are not directly comparable. In the worst case, lease skew that lets two nodes simultaneously believe they hold the same lease can corrupt whatever state that lease was coordinating.

Triggers

  • ·NTP server unavailable or unreachable for an extended period
  • ·VM migration causing a stale clock on the resumed instance
  • ·Hypervisor time adjustment pushing the guest clock forward or backward
  • ·ntpd applying a step correction (a discontinuous jump) rather than a slew correction
  • ·Container or pod with no direct NTP access inheriting host clock drift

Detection Signals

log errorsalert

Mitigation Strategies

Configure and monitor NTP synchronization on every nodepreventscomplexity: low

Run chrony or ntpd on every host and container host, and monitor the offset metric directly (chronyc tracking, node_exporter), alerting when it exceeds a set tolerance (for example 100ms). Use the cloud provider's internal time-sync endpoint where available (AWS EC2: the Amazon Time Sync Service) rather than an external NTP pool, since the extra network hop to an external source adds its own skew.

Use a monotonic clock for duration and timeout logicpreventscomplexity: low

Measure elapsed time (has this operation run too long, has this lease expired locally) with a monotonic clock source (CLOCK_MONOTONIC), not wall-clock time (CLOCK_REALTIME). A monotonic clock cannot move backward from an NTP step correction, so a duration computed from it cannot go negative. Wall-clock time is still correct for anything that must be compared across nodes or persisted.

Use logical clocks or Hybrid Logical Clocks for cross-node event orderingpreventscomplexity: high

Replace wall-clock timestamps with Lamport clocks or HLCs when ordering events across nodes matters for correctness, not just for display. HLCs keep a human-readable timestamp bounded close to physical time while adding causal ordering that does not depend on clock synchronization. CockroachDB's HLC implementation is a reference design.

Add explicit skew tolerance in token and lease validationcomplexity: low

Allow a configurable tolerance window (for example 5 minutes for JWTs, low tens of milliseconds for lease timeouts) in validation logic, rejecting only tokens or leases expired or future-dated beyond that window. This absorbs ordinary skew but does not fix the underlying dual-lease-holder hazard; pair it with fencing tokens wherever a lease grants exclusive write access, per leader_election.

Recovery Steps

  1. 1.Check clock offset on all nodes: chronyc tracking | grep offset
  2. 2.Identify nodes with offset beyond tolerance (for example, over 100ms): these are the affected nodes
  3. 3.Restart ntpd or chrony on affected nodes to force resynchronization
  4. 4.For VMs: check hypervisor time-sync settings; on AWS confirm Amazon Time Sync Service is enabled
  5. 5.After sync, verify offset returns within tolerance (for example under 10ms) before routing production traffic back to affected nodes

Estimated recovery time: Small drift corrects within seconds via slew adjustment. A large step correction (over a second) may take minutes under slew mode, or apply instantly under step correction, which is the discontinuity to watch for on duration-sensitive code. Application-level symptoms resolve immediately once the underlying clock is corrected.

Affected Systems

Patterns

leader electiontwo phase commit

Technologies

kafkacassandrapostgresql

Basis

Well-documented distributed-systems failure; the drift-versus-skew distinction and monotonic-versus-wall-clock guidance are standard operating-systems and distributed- systems material; AWS, Google Spanner (TrueTime and commit wait), and CockroachDB (HLC) documentation cover the mechanisms in detail.