In distributed systems, physical time is unreliable due to clock drift and network latency. To reason about the sequence of events, we use logical time, which focuses on causality—the "happened-before" relation—rather than wall-clock time.
The relation\todefines a strict partial order on events in a system. Formally, for two eventsaandb:
Ifa \not\to bandb \not\to a, thenaandbare concurrent (a \parallel b).
A Lamport clock is a simple monotonically increasing counter maintained by each process.
Each processP_imaintains a local counterL_i.
Note: The converse is NOT true. IfL(a) < L(b), we cannot concludea \to b. They could be concurrent.
To detect concurrency (i.e., to make the clock condition a bidirectional implication), we use Vector Clocks.
In a system withNprocesses, each processP_imaintains a vectorV_iof sizeN, whereV_i[j]isP_i's knowledge of the clock of processP_j.
And then increments its own component:
Concurrency Detection: Eventsaandbare concurrent (a \parallel b) if and only ifV(a) \not\le V(b)andV(b) \not\le V(a). In other words, the vectors are incomparable.
Dynamo-style databases (e.g., Riak, Cassandra) use vector clocks (or the optimized "Dotted Version Vectors") to detect concurrent writes to the same key. If two versions of an object have incomparable vector clocks, the system knows a conflict has occurred and can trigger "Sibling" resolution or manual reconciliation.
By attaching vector clocks to data, a system can ensure that a user never sees an "effect" before its "cause." For example, if a comment is a reply to a post, the reply will only be shown if the post is already visible in the local view.