Loading content...
In a perfect world, every write to a database would happen in perfect isolation—one at a time, in a well-defined order, with instant global visibility. Reality, however, is far more complex. The moment you distribute data across multiple nodes, replicas, or data centers, you enter a world where write conflicts are not just possible—they are inevitable.
Understanding write conflicts is fundamental to designing robust distributed systems. These conflicts arise not from bugs or poor design, but from the fundamental physics of distributed computing: information takes time to travel, nodes can fail independently, and the speed of light is finite. Every distributed database, every multi-master replication setup, every collaborative editing system must confront and resolve these conflicts.
By the end of this page, you will understand the fundamental nature of write conflicts, why they occur in distributed systems, how to identify and classify them, and the profound impact they have on system design. This foundation is essential before we explore specific resolution strategies in subsequent pages.
A write conflict occurs when two or more concurrent write operations target the same piece of data without coordination, resulting in a state where the system cannot deterministically decide which value should prevail. Let's dissect the precise conditions that create conflicts.
The Three Prerequisites for Conflict:
For a write conflict to occur, three conditions must be present simultaneously:
Concurrency — Two or more write operations happen 'at the same time' in the distributed sense (neither is aware of the other when it begins)
Same Target — Both writes affect the same logical data item (the same key, the same row, the same document field)
No Coordination — The operations proceed without a synchronization mechanism that would serialize them (no locks, no leader election, no consensus)
Why 'Concurrent' Is Complex:
In distributed systems, 'concurrent' doesn't mean 'at the exact same nanosecond.' It means causally independent—neither operation could have influenced the other. Consider:
Are these concurrent? The answer depends on perspective:
This distinction is crucial. Physical clocks can drift (sometimes by seconds), network latency varies, and there's no global time authority in a distributed system. Two operations that appear sequential in wall-clock time may be logically concurrent if the causal relationship cannot be established.
Leslie Lamport's seminal 1978 paper 'Time, Clocks, and the Ordering of Events in a Distributed System' formalized this: event A 'happens before' event B (written A → B) if A could have causally influenced B. Two events are concurrent if neither happened before the other (A || B). This is the theoretical foundation for understanding conflicts.
Write conflicts emerge from specific architectural patterns in distributed systems. Understanding these sources helps you anticipate where conflicts will occur and design appropriate resolution strategies.
| Architecture | Conflict Risk | Conflict Frequency | Typical Resolution |
|---|---|---|---|
| Single Leader (sync replication) | Very Low | Rare (only during failover) | Rollback or manual |
| Single Leader (async replication) | Low | Low (lag-induced) | Last-write-wins or rollback |
| Multi-Leader | High | Frequent | LWW, vector clocks, or CRDTs |
| Leaderless (quorum) | Medium-High | Moderate | Read repair, anti-entropy |
| Offline-First | Very High | Very Frequent | Operational transforms, CRDTs |
The Trade-off Spectrum:
Every architectural decision that increases availability or reduces latency also increases conflict potential:
This isn't a flaw—it's a fundamental trade-off governed by the CAP theorem. The key is choosing the right point on this spectrum for your use case and having a robust strategy for the conflicts you'll encounter.
Not all conflicts are created equal. Understanding the different types of conflicts helps you choose appropriate resolution strategies and design data models that minimize harmful conflicts.
Write-Write Conflicts (also called update conflicts) occur when two operations concurrently modify the same data item.
Example Scenario:
balance = balance + 100 (deposit $100)balance = balance - 50 (withdraw $50)balance = 500 before their operation600, B writes 450The Problem: If we take either value, we lose the other operation. The correct final balance should be 550, but neither 600 nor 450 is correct.
Characteristics:
1234567891011121314151617181920
// Initial state: { balance: 500 } // Client A's operation (runs on Replica 1)function deposit(amount) { const current = read("balance"); // reads 500 write("balance", current + amount); // writes 600}deposit(100); // Client B's operation (runs on Replica 2, concurrently)function withdraw(amount) { const current = read("balance"); // reads 500 (hasn't seen A's write) write("balance", current - amount); // writes 450}withdraw(50); // After replication sync:// Replica 1 has: balance = 600// Replica 2 has: balance = 450// CONFLICT: Which is correct? Neither! Should be 550.Before you can resolve a conflict, you must detect it. Distributed systems employ various mechanisms to identify when concurrent writes have created divergent states.
Version Vectors in Depth:
Version vectors are the most rigorous conflict detection mechanism. Each replica maintains a vector: {Replica1: 5, Replica2: 3, Replica3: 7}. When data is modified:
Example:
{R1: 5, R2: 3, R3: 7}{R1: 4, R2: 4, R3: 7}A has R1:5 > R1:4, but B has R2:4 > R2:3. Neither dominates → Concurrent writes detected!
1234567891011121314151617181920212223242526272829303132333435363738
interface VersionVector { [replicaId: string]: number;} type ComparisonResult = 'DOMINATES' | 'DOMINATED_BY' | 'CONCURRENT' | 'EQUAL'; function compareVersionVectors( vectorA: VersionVector, vectorB: VersionVector): ComparisonResult { const allKeys = new Set([ ...Object.keys(vectorA), ...Object.keys(vectorB) ]); let aDominates = true; let bDominates = true; for (const key of allKeys) { const a = vectorA[key] ?? 0; const b = vectorB[key] ?? 0; if (a < b) aDominates = false; if (b < a) bDominates = false; } if (aDominates && bDominates) return 'EQUAL'; if (aDominates) return 'DOMINATES'; if (bDominates) return 'DOMINATED_BY'; return 'CONCURRENT'; // Neither dominates = CONFLICT} // Usage example:const vectorA: VersionVector = { replica1: 5, replica2: 3, replica3: 7 };const vectorB: VersionVector = { replica1: 4, replica2: 4, replica3: 7 }; const result = compareVersionVectors(vectorA, vectorB);console.log(result); // Output: 'CONCURRENT' - Conflict detected!When conflicts go unresolved or are resolved incorrectly, the consequences cascade through your system. Understanding these impacts underscores why robust conflict resolution is not optional.
{balance: 600} and {balance: 450} might produce {balance: 525} (average), which is still wrong.In Amazon's original Dynamo paper, they describe how shopping cart conflicts were resolved by taking the union of all concurrent cart states. This means if you remove an item from your cart during a conflict, it might reappear—frustrating, but Amazon judged it better than items silently disappearing. This is a conscious trade-off between conflict resolution strategies.
| Domain | Conflict Example | Potential Impact | Acceptable Strategy |
|---|---|---|---|
| E-commerce Cart | Item quantity conflicts | Customer frustration | Union/addition (items reappear) |
| Banking | Concurrent transfers | Financial loss | Strong consistency required |
| Social Media | Concurrent likes/comments | Counter inaccuracy | CRDTs, eventual accuracy |
| Collaborative Docs | Concurrent text edits | Lost changes | Operational transforms |
| Inventory | Concurrent stock updates | Overselling, negative stock | Requires coordination |
| Medical Records | Concurrent updates | Patient safety risk | Strong consistency + audit |
Conflict resolution strategies exist on a spectrum from simple but lossy to complex but precise. Your choice depends on data semantics, consistency requirements, and operational complexity tolerance.
Choosing Your Strategy:
The right conflict resolution strategy depends on multiple factors:
| Factor | Favors Simple (LWW) | Favors Complex (CRDTs) |
|---|---|---|
| Data semantics | Last value is all that matters | History/accumulation matters |
| Conflict frequency | Rare (mostly sequential) | Frequent (high concurrency) |
| Data criticality | Low (logs, metrics) | High (financial, medical) |
| Team expertise | Limited distributed experience | Strong distributed expertise |
| Latency requirements | Can tolerate coordination | Needs low latency always |
Production systems often use hybrid approaches: CRDTs for counters and sets, LWW for simple values, and application-level merge for complex business objects. The key is matching the resolution strategy to the semantic requirements of each data type.
The best conflict is the one that never happens. While you cannot eliminate all conflicts in a distributed system, thoughtful design significantly reduces their frequency and impact.
counter = 5, use increment counter by 1. Increments from different sources can be safely merged. This is the foundation of CRDTs.1234567891011121314151617181920212223242526272829
// CONFLICT-PRONE: Updating shared counterinterface ConflictProne { shoppingCart: { totalItems: number; // Concurrent updates conflict! items: Item[]; };} // CONFLICT-RESISTANT: Each user has own partitioninterface ConflictResistant { userCarts: { [userId: string]: { // Only this user modifies their cart items: Item[]; }; };} // CONFLICT-FREE: Operation-based updatesinterface CartOperation { type: 'ADD_ITEM' | 'REMOVE_ITEM' | 'UPDATE_QUANTITY'; itemId: string; userId: string; timestamp: number; delta?: number; // For quantity: +1, -1, etc.} // Operations can be merged: apply all ADDs, then all REMOVEs// Final state is deterministic regardless of operation orderWrite conflicts in distributed systems are not bugs—they are an inherent consequence of distributing data across space and accepting writes in multiple locations. Let's consolidate what we've learned:
What's Next:
Now that we understand the nature and impact of write conflicts, we're ready to explore specific resolution strategies. In the next page, we'll deep-dive into Last-Write-Wins (LWW)—the simplest and most common resolution strategy, its mechanics, trade-offs, and when it's appropriate.
You now understand the fundamental nature of write conflicts in distributed systems—why they occur, how to classify them, how to detect them, and their potential impact. This knowledge is essential for choosing and implementing appropriate resolution strategies in the pages ahead.