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 \to defines a strict partial order on events in a system. Formally, for two events a and b:
If a \not\to b and b \not\to a, then a and b are concurrent (a \parallel b).
A Lamport clock is a simple monotonically increasing counter maintained by each process.
Each process P_i maintains a local counter L_i.
Note: The converse is NOT true. If L(a) < L(b), we cannot conclude a \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 with N processes, each process P_i maintains a vector V_i of size N, where V_i[j] is P_i's knowledge of the clock of process P_j.
And then increments its own component:
Concurrency Detection: Events a and b are concurrent (a \parallel b) if and only if V(a) \not\le V(b) and V(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.
Cluster Hub: Distributed Systems Hub (distributed-systems)