Time & Clock Synchronization
Time is the foundation upon which all distributed systems reasoning is built. Understanding clocks is crucial for designing systems that maintain consistency and order. Here is the uncomfortable truth that makes this chapter important: there is no “now” in a distributed system. On a single computer, “now” is a meaningful concept — you can read the clock and trust it. In a distributed system, each machine has its own clock, each clock drifts differently, and the act of asking another machine “what time is it?” takes a non-zero amount of time. This means that every timestamp-based decision (who wrote first? has this lease expired? is this cache entry stale?) is fundamentally uncertain. The rest of this chapter is about different strategies for coping with that uncertainty.Key Topics: Physical Clocks, NTP, PTP, Logical Clocks, Vector Clocks, HLC, TrueTime
Interview Focus: Google Spanner’s TrueTime, causality tracking, clock synchronization trade-offs
The Fundamental Problem
Module 33: Clock Synchronization Protocols
Physical Clock Synchronization
Network Time Protocol (NTP)
NTP is the most widely used clock synchronization protocol on the internet.NTP Synchronization Algorithm
Precision Time Protocol (PTP - IEEE 1588)
For applications requiring sub-microsecond accuracy:Clock Anomalies and Edge Cases
Real-World Clock Problems
Leap Seconds
Leap Seconds
Clock Jumps (Step Changes)
Clock Jumps (Step Changes)
NTP Slewing
NTP Slewing
VM Clock Issues
VM Clock Issues
Module 34: Logical and Vector Clocks
Logical Clocks Deep Dive
Lamport Timestamps
Vector Clocks
Vector clocks capture causality - they can tell you if two events are concurrent:Vector Clock in DynamoDB/Riak
Module 35: Hybrid Logical Clocks (HLC)
Used by CockroachDB, MongoDB, and many modern databases:Module 36: TrueTime and Atomic Clocks
Google TrueTime
The gold standard for distributed time synchronization:Spanner’s External Consistency
Fault-Tolerant Clock Synchronization
In large-scale systems, some nodes might have faulty clocks or even malicious intent (Byzantine failures).Marzullo’s Algorithm
Marzullo’s algorithm is used to select a confidence interval from a set of noisy time sources.The Uncertainty Window & Intersection
In high-accuracy systems (like PTP or TrueTime), the clock is never a point, but an interval: .The Overlap Rule
For two events and to be definitively ordered (), their uncertainty intervals must not overlap: If they overlap, the system cannot determine the true order based on physical time alone. This is the Causality Gap.Multi-Source Intersection (The “Master” Interval)
When a node queries time sources, it uses Marzullo’s (or the Improved Marzullo/Intersection algorithm) to find the “True” interval. If the sources disagree significantly, the interval grows (increasing uncertainty) or the node enters a “Panic” state. Staff Tip: When designing systems that rely on time for consistency (like Spanner), you must explicitly handle the “Overlap Case” by either:- Waiting: Wait until the uncertainty window of has passed before starting (Commit-Wait).
- Versioning: Use a logical counter (HLC) to break ties during the overlap.
Byzantine Clock Synchronization
If nodes are Byzantine (malicious), we need at least total nodes to synchronize clocks correctly.- Lynch-Welch Algorithm: Nodes exchange their clock values. Each node discards the highest and lowest values and takes the average of the remaining values.
- Clock Drift Bounds: In a Byzantine environment, the maximum skew between correct clocks is bounded by , where is message delay uncertainty, is drift rate, and is synchronization interval.
Practical Guidelines
Choosing the Right Clock
- Use Physical Time When...
- Use Logical Clocks When...
- Use HLC When...
- Use TrueTime When...
- Generating user-facing timestamps
- Debugging and logging
- TTL (time-to-live) calculations
- Scheduling future events
- Audit trails and compliance
Clock Best Practices
Interview Questions
Q: How does Google Spanner achieve external consistency?
Q: How does Google Spanner achieve external consistency?
- TrueTime API: Returns time interval [earliest, latest] instead of single value
- GPS + Atomic Clocks: Hardware in every datacenter for accurate time
- Commit Wait: After getting timestamp, wait until uncertainty period passes
- Guarantee: If T1 commits before T2 starts, T1’s timestamp < T2’s timestamp
Q: Why can't we just use physical timestamps for ordering?
Q: Why can't we just use physical timestamps for ordering?
- Clock Skew: Different machines have different times (milliseconds to seconds)
- Clock Drift: Clocks run at slightly different speeds
- NTP Jumps: Clocks can jump forward or backward during sync
- No Causality: Physical time doesn’t capture happened-before relationships
Q: When would you use vector clocks vs HLC?
Q: When would you use vector clocks vs HLC?
- When you need precise conflict detection
- Systems like DynamoDB/Riak that return conflicting versions
- When number of nodes is bounded and small
- When you need approximate physical time for debugging
- Systems with many nodes (vector clock size = O(n))
- Databases like CockroachDB, MongoDB
- When snapshot isolation is needed
Q: Design a distributed system that orders events across data centers
Q: Design a distributed system that orders events across data centers
- Understand requirements: Strong ordering (expensive) vs causal ordering (cheaper)
-
For causal ordering:
- Use HLC at each data center
- Propagate timestamps with messages
- Compare HLC timestamps for ordering
-
For strong ordering:
- Central sequencer (single point of failure)
- OR distributed consensus (high latency)
- OR TrueTime-like approach (hardware investment)
-
Practical trade-off:
- Use causal ordering where possible
- Strong ordering only where required (e.g., financial transactions)
Key Takeaways
Perfect Time is Impossible
Causality ≠ Physical Time
Measure and Monitor
Choose the Right Tool
Interview Deep-Dive
You are building a multi-region database and your team is debating whether to use Hybrid Logical Clocks or invest in a TrueTime-like infrastructure. Walk me through the trade-offs.
You are building a multi-region database and your team is debating whether to use Hybrid Logical Clocks or invest in a TrueTime-like infrastructure. Walk me through the trade-offs.
- HLC gives you causality tracking (if event A causes event B, HLC guarantees A’s timestamp is less than B’s) plus a close approximation of physical time, all in software with zero hardware investment. CockroachDB and MongoDB both use HLC. The downside is that HLC cannot provide external consistency — if two unrelated transactions happen in different regions, HLC cannot guarantee their timestamps reflect real-world ordering because the physical clocks they are based on have unbounded skew (in theory).
- TrueTime gives you bounded uncertainty intervals — the system knows the actual time is within a window (typically 1-7ms). This allows Spanner’s commit-wait protocol: after assigning a timestamp, the transaction waits until the uncertainty interval has passed, guaranteeing that no future transaction can receive a lower timestamp. This achieves external consistency. The cost is GPS receivers and atomic clocks in every data center, plus the latency overhead of the commit-wait (equal to the uncertainty interval).
- For most companies, HLC is the right choice. External consistency matters only when you need globally ordered transactions across independent shards with no causal relationship. If your workload can tolerate “causal consistency” rather than “strict serializable across unrelated transactions,” HLC is sufficient and dramatically simpler to operate.
- The CockroachDB compromise is instructive: they use HLC but also enforce a maximum clock skew bound. If a node’s clock drifts beyond that bound, it self-quarantines. This provides “external consistency within the skew bound” without specialized hardware.
Explain Lamport timestamps and their fundamental limitation. Then explain how vector clocks fix that limitation.
Explain Lamport timestamps and their fundamental limitation. Then explain how vector clocks fix that limitation.
- Lamport timestamps assign a single integer counter to each event. The rule is: before any event, increment the counter; when sending a message, attach the counter; when receiving, set your counter to max(local, received) + 1. This guarantees that if event A happened-before event B (causally), then L(A) is less than L(B).
- The fundamental limitation is the converse is not true: L(A) less than L(B) does NOT imply A happened before B. Two completely independent events on different nodes can have ordered timestamps by coincidence. You cannot distinguish “A caused B” from “A and B were concurrent but A happened to get a lower number.” This means Lamport timestamps cannot detect conflicts.
- Vector clocks fix this by maintaining one counter per node. Each node increments only its own entry. When sending, it attaches the entire vector. When receiving, it takes the element-wise max and increments its own entry. Now you can compare two vectors: if every entry in V1 is less than or equal to V2, and at least one is strictly less, then V1 happened before V2. If neither dominates the other (V1 has some entries greater, V2 has others greater), the events are concurrent — a true conflict.
- The trade-off is size: vector clocks grow linearly with the number of nodes. For a system with thousands of nodes, this overhead is prohibitive. This is why systems like DynamoDB originally used vector clocks but later moved to simpler mechanisms (last-writer-wins with server-side timestamps).
Walk me through exactly how Google Spanner's commit-wait protocol works and why it guarantees external consistency.
Walk me through exactly how Google Spanner's commit-wait protocol works and why it guarantees external consistency.
- When a Spanner transaction is ready to commit, it acquires locks on all participants, then gets a commit timestamp
s = TT.now().latest— the upper bound of the current TrueTime uncertainty interval. - Then it waits. Specifically, it waits until
TT.after(s)returns true, meaning the system is now certain that timeshas definitively passed on every node in the world. This wait is typically 1-7ms, equal to twice the TrueTime uncertainty epsilon. - After the wait, the transaction’s effects are made visible and locks are released.
- Why this guarantees external consistency: suppose transaction T1 commits with timestamp s1 and then a client, having observed T1’s commit, starts transaction T2. T2 starts at real-time
t2, which is after T1’s commit-wait completed. Thereforet2 > s1in absolute real time. When T2 callsTT.now(), the returned interval will haveearliest >= t2 - epsilon > s1(because we waited untils1was definitely in the past). So T2’s commit timestamps2 = TT.now().latest >= t2 > s1. This guarantees s2 > s1, meaning T2 is ordered after T1 in the commit order. - The brilliance is that this works without any coordination between T1 and T2 — they could be on different continents, different Paxos groups. The global ordering comes from physics (bounded clock uncertainty) rather than communication.