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.
