Skip to content
Vector Clocks and Conflict Resolution: Tracking Causality in Distributed Systems 4 min

Vector Clocks and Conflict Resolution: Tracking Causality in Distributed Systems

SD
ScaleDojo
May 11, 2026
4 min read910 words
Vector Clocks and Conflict Resolution: Tracking Causality in Distributed Systems

The Amazon Shopping Cart Problem

Amazon's Dynamo paper (2007) describes this scenario: a user adds item A to their cart on their phone, then adds item B on their laptop. Both writes go to different Dynamo replicas. When the replicas sync, which cart is correct? The phone version (with A) or the laptop version (with B)? Neither - the correct answer is both A and B. Vector clocks tell Dynamo these writes are concurrent (neither caused the other), so it must merge them.

This is a strange thing to sit with at first. Two things can both be "correct" at the same time, with no order between them at all. That idea-that some events genuinely have no before-or-after relationship-is the entire reason vector clocks exist.

Why Wall Clocks Cannot Be Trusted

The obvious instinct is to just timestamp every write and sort by time. That instinct is wrong, and it's worth seeing exactly why.

The Clock Problem: Server A's clock: 10:00:01.000
Server B's clock: 10:00:01.050

Event on A at 10:00:01.000
Event on B at 10:00:01.050

Did A happen before B? MAYBE.
What if B's clock is 100ms ahead?
Then B actually happened at 10:00:00.950 (BEFORE A).

Clock drift causes:
- NTP sync only accurate to ~1ms within DC, ~10ms across DCs
- Leap seconds cause clocks to jump
- VM live migration can warp time
- Cloud providers make no hard clock guarantees

Using timestamps for ordering? You WILL get it wrong.

Lamport Clocks: Simple Logical Ordering

Leslie Lamport (1978) proposed a simple alternative: forget physical time. Use a single counter that increments on every event and takes the maximum when messages are received.

Lamport Clock:

  Node A (counter=0)          Node B (counter=0)
  |
  | event: A writes X=1
  | counter++ -> 1
  |
  |------- send msg(counter=1) ------>
  |                                   | receive: max(0,1)+1 = 2
  |                                   |
  |                                   | event: B writes Y=2
  |                                   | counter++ -> 3
  |
  | event: A writes X=5
  | counter++ -> 2

  Limitation: Lamport clock says A-write-X=5 (counter=2)
  happened BEFORE B-write-Y=2 (counter=3).
  But they are actually CONCURRENT events!
  Lamport clocks cannot detect concurrency.

Lamport Clocks are a genuine improvement over wall-clock timestamps -they don't drift, and they respect message order. But look at that limitation closely: the counters say one thing happened before the other, and that's simply false. A single number can order events, but it can't tell you when there was no order to begin with.

Vector Clocks: Detecting Concurrency

A vector clock is a list of counters, one per node. Each node increments its own counter on every event. When nodes communicate, they merge their vectors (taking the max of each counter). This lets you detect both causality AND concurrency.

Vector Clock with 3 nodes [A, B, C]:

  Node A              Node B              Node C
  [0, 0, 0]          [0, 0, 0]          [0, 0, 0]
  |
  | A writes X=1
  | [1, 0, 0]
  |
  |---msg(vc=[1,0,0])--->
  |                     | merge: max([0,0,0],[1,0,0])+B
  |                     | [1, 1, 0]
  |                     |
  |                     | B writes Y=2
  |                     | [1, 2, 0]
  |
  | A writes X=5                          | C writes Z=3
  | [2, 0, 0]                            | [0, 0, 1]

  Compare [2, 0, 0] vs [0, 0, 1]:
  A's counter: 2 > 0  (A wins)
  C's counter: 0 < 1  (C wins)
  NEITHER dominates --> CONCURRENT! (conflict)

  Compare [1, 0, 0] vs [1, 2, 0]:
  A's counter: 1 <= 1
  B's counter: 0 <= 2
  [1,0,0] <= [1,2,0] --> A happened BEFORE B (causal)

This is the whole trick, laid out plainly: when one vector is fully greater-or-equal to another across every position, you have real causality-one event genuinely led to the other. When neither vector dominates, you have a true conflict, and no amount of squinting at timestamps would have told you that. Vector Clocks vs Lamport Clocks really comes down to this one capability: a single counter can't represent "neither happened first," but a vector can.

Conflict Resolution Strategies

When vector clocks detect a conflict:

  Strategy             How                         Used By
  -------------------  --------------------------  ----------
  Last-Writer-Wins     Attach timestamp, keep       Cassandra
  (LWW)               latest. LOSES DATA.          (default)

  Multi-value          Keep ALL versions,           DynamoDB
  (siblings)           return to client.            Riak
                       Client/app resolves.

  CRDT merge           Mathematically merge         Redis CRDT
  (auto-resolve)       using commutative rules.     Riak (sets)
                       No human needed.

  Application merge    Custom logic per data type.  Git
                       Like git merge for data.     Custom apps

  Shopping cart example (Amazon Dynamo):
  Version A: {item1, item2}     vc=[2, 0]
  Version B: {item1, item3}     vc=[0, 2]
  Concurrent! Merge: UNION = {item1, item2, item3}
  Both items preserved. No data lost.

Notice these Conflict Resolution Strategies aren't ranked best-to-worst — they trade off differently. Last-Writer-Wins is the simplest and the one most likely to silently throw data away. Multi-value sidesteps that by keeping everything and pushing the decision back to the client. CRDTs are the elegant middle ground: the merge rule is baked into the data type itself, so nothing needs to ask a human at all.

Interview Tip

When discussing conflict resolution, say: 'I would use vector clocks to detect whether writes are causally ordered or concurrent. For concurrent writes, the resolution strategy depends on the data type. For shopping carts or sets, I would merge using union - no data lost. For user profiles where only the latest matters, last-writer-wins with a logical timestamp works. The key is that vector clocks DETECT the conflict - what you do about it depends on your domain.'

Key Takeaway

Vector clocks track causality without relying on synchronized wall clocks. They detect whether two events are causally related or concurrent (conflicting). When conflicts are detected, resolution strategies (merge, LWW, or user intervention) determine the final value.

Enjoyed this article?

Share it with your network to help others level up their system design skills.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!

Related Articles

Enjoyed this content?

Your support keeps us creating free resources

We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.

$

One-time payment via Stripe. ScaleDojo account required.

ScaleDojo Logo
Initializing ScaleDojo