Knowledge Guide
HomeSystem DesignScalable Systems (Advanced Topics)

What Are Common Conflict Resolution Strategies Last‑write‑wins, Vector Clocks, Logical Clocks

When two replicas accept writes to the same key without coordinating, you get two values and no built-in notion of which came first — so every conflict-resolution scheme is really a way of manufacturing an order over writes that arrived without one. Last-write-wins orders by a scalar timestamp and keeps the max; Lamport clocks order by a counter that respects cause-and-effect but flattens everything into a single line; vector clocks keep one counter per writer so they can tell "A caused B" apart from "A and B happened independently," which is the only way to detect a conflict instead of silently discarding one side.

The choice is a trade between metadata cost and how much you care about losing a concurrent write. The three strategies below are the standard ladder from cheapest-and-lossy to expensive-and-lossless, followed by the two techniques (hybrid logical clocks, CRDTs) that production systems actually reach for.

Last-write-wins (LWW): order by a timestamp, keep the max

Each write is tagged with a timestamp — usually wall-clock microseconds — and on conflict the value with the larger timestamp wins; the loser is dropped. Apache Cassandra does exactly this per column: every cell carries a write timestamp, and a read that finds two cells for the same column returns the one with the newest timestamp (breaking exact ties by comparing the value bytes, so the result is deterministic but arbitrary).

It is the cheapest possible scheme: one number per write, resolution is a single comparison, no versions to keep, no merge to run. The cost is baked in — resolving by "latest" means throwing away every concurrent update but one. If Alice sets a field on replica R1 and Bob sets the same field on R2 within the same instant, one of them is deleted with no trace. And because the order is a physical timestamp, LWW is only as trustworthy as your clocks: a node whose clock runs fast wins conflicts it shouldn't, and an NTP step that moves a clock backward can make a genuinely newer write look older and be overwritten by stale data.

Vector clocks: one counter per node, so concurrency is visible

Each node keeps a vector of counters — one slot per writer, e.g. [A,B,C]. The rules are tiny: on a local write, a node increments its own slot; when it receives another node's version, it takes the element-wise max of the two vectors before applying its own increment. To compare two versions X and Y you use the partial-order rule:

That last case is the whole point: a vector clock cannot merge data for you, but it can prove that two writes were independent, which LWW and Lamport clocks fundamentally cannot. Correction to a common myth: the original Amazon Dynamo paper (2007) and Riak used vector/version vectors to expose siblings — but the managed DynamoDB product does not; it resolves conflicts with last-write-wins by default. Don't cite "DynamoDB uses vector clocks."

Traced example: a shopping cart on three replicas

Vectors are [A,B,C]. A client adds items through whichever replica it reaches; replicas gossip writes to each other.

StepActionVector afterCart value
1Client adds milk via replica A → A bumps its own slotA = [1,0,0]{milk}
2A's write replicates to B and C; both store version [1,0,0]B, C know [1,0,0]{milk}
3Client adds eggs via B → merge max([1,0,0],[1,0,0])=[1,0,0], then bump B's slotB = [1,1,0]{milk, eggs}
4Concurrently, client adds bread via C (never saw B's write) → bump C's slotC = [1,0,1]{milk, bread}
5Read reconciles [1,1,0] vs [1,0,1]: slot B is 1>0, slot C is 1>0 → neither ≤ the other → CONCURRENTsiblings kept{milk,eggs} & {milk,bread}

Under LWW, step 5 would keep only whichever write had the larger timestamp — eggs or bread would vanish. The vector clock instead surfaces both siblings so the app (or a CRDT) can union them into {milk, eggs, bread}. Note the classic pathology: if the client always writes without first reading and merging, siblings pile up on every write — sibling explosion.

diagram
diagram

Lamport clocks: one counter, causal order but concurrency-blind

A Lamport logical clock is a single integer per process. On any local event a process does L = L + 1; when it sends a message it attaches L; on receive it sets L = max(L_local, L_received) + 1. This guarantees the one-way property: if event a happened-before b, then L(a) < L(b). The converse does not hold — a smaller timestamp does not mean it happened-before.

Trace showing the blind spot. Process P1 does event a: L=1, and sends a message carrying 1 to P2. P2 receives it: L = max(0,1)+1 = 2, event b. Separately, P3 — which never talked to anyone — does a local event c: L=1. Now compare b (2) and c (1): the numbers say c < b, implying c came first. But c and b are genuinely concurrent — no message ever linked them. Lamport's single counter has flattened two unrelated timelines into one arbitrary line. Using Lamport timestamps for conflict resolution is therefore functionally LWW (highest counter wins) — it just replaces the wall clock, so it dodges clock skew while inheriting LWW's silent data loss.

Beyond the three: HLC and CRDTs

Hybrid logical clocks (HLC) pack a physical timestamp and a small logical counter into one comparable value. They order events by wall time when clocks are close, and fall back to the counter to break ties and preserve causality when clocks disagree — giving you human-meaningful ordering plus a bound on how badly skew can hurt. CockroachDB and YugabyteDB use HLCs for exactly this. But HLC still assumes clock skew stays under a configured bound; if a node's clock jumps past that bound, the guarantee breaks.

CRDTs (conflict-free replicated data types) attack the other half of the problem. Vector clocks detect a conflict but leave the merge to you; a CRDT defines a merge that is commutative, associative, and idempotent, so replicas that saw the same writes in any order converge to the same value with no coordination and no lost update. A grow-only set (G-Set) unions its elements; an OR-Set tracks add/remove tags so a concurrent add and remove resolve deterministically. The shopping cart above is naturally an OR-Set: {milk,eggs} ⊔ {milk,bread} = {milk,eggs,bread}, automatically. The cost is that not every data type has a clean CRDT, and metadata (tombstones, causal context) accumulates.

Pitfalls a working engineer hits

When to use which — and what each costs

StrategyMetadataDetects concurrency?Data loss riskMain cost
LWW (wall clock)1 timestampNoHigh — drops all but oneClock skew corrupts ordering
Lamport clock1 integerNoHigh (like LWW)Flattens concurrency; no skew problem
Vector clockN counters (per writer)Yes → siblingsNone if you merge siblingsMetadata grows with writers; you still write the merge
CRDTType-dependent (tags, tombstones)Merges deterministicallyNoneNot all types fit; metadata accretes

Concrete decision signals:

Takeaways


Re-authored/Deepened for this guide. Sources: Leslie Lamport, "Time, Clocks, and the Ordering of Events in a Distributed System" (CACM, 1978); DeCandia et al., "Dynamo: Amazon's Highly Available Key-value Store" (SOSP, 2007); Shapiro et al., "Conflict-free Replicated Data Types" (INRIA, 2011); Kulkarni et al., "Logical Physical Clocks" (HLC, 2014); Martin Kleppmann, "Designing Data-Intensive Applications" (ch. 5, replication & conflict resolution); Basho/Riak documentation on version vectors, siblings, and dotted version vectors; Apache Cassandra and CockroachDB documentation.

🤖 Don't fully get this? Learn it with Claude

Stuck on What Are Common Conflict Resolution Strategies Last‑write‑wins, Vector Clocks, Logical Clocks? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **What Are Common Conflict Resolution Strategies Last‑write‑wins, Vector Clocks, Logical Clocks** (System Design). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **What Are Common Conflict Resolution Strategies Last‑write‑wins, Vector Clocks, Logical Clocks** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **What Are Common Conflict Resolution Strategies Last‑write‑wins, Vector Clocks, Logical Clocks**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **What Are Common Conflict Resolution Strategies Last‑write‑wins, Vector Clocks, Logical Clocks**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes