Last modified: March 23, 2026
This article is written in: πΊπΈ
Concurrent writes happen when two or more clients write to the same key in a database at the same time, each unaware of the other's write. In replicated systems, these writes may arrive at different replicas in different orders, causing the replicas to diverge and hold conflicting values. Without a strategy to detect and resolve these conflicts, the system risks permanent inconsistency across its nodes.
Client A writes X=1 Client B writes X=2
at time T at time T
| |
v v
+------------+ +------------+
| Replica 1 | | Replica 2 |
| X = 1 | | X = 2 |
+------+-----+ +------+-----+
| |
| (replication lag) |
v v
+------------+ +------------+
| Replica 1 | | Replica 2 |
| X = 1? 2? | | X = 2? 1? |
+------------+ +------------+
Both replicas received both writes but in
different orders β which value is correct?
Timeline at Node 1 Timeline at Node 2
t1: Receive Write A t1: Receive Write B
(X = "apple") (X = "banana")
| |
t2: Receive Write B t2: Receive Write A
(X = "banana") (X = "apple")
| |
v v
Final state: X = "banana" Final state: X = "apple"
β β
| |
INCONSISTENT β replicas disagree!
Client A: SET X = "apple" (timestamp: 100)
Client B: SET X = "banana" (timestamp: 105)
+---------------------+ +---------------------+
| Replica 1 | | Replica 2 |
| | | |
| Receives: | | Receives: |
| A (ts=100) first | | B (ts=105) first |
| B (ts=105) second | | A (ts=100) second |
| | | |
| Keeps: B (ts=105) | | Keeps: B (ts=105) |
| X = "banana" β | | X = "banana" β |
+---------------------+ +---------------------+
Both replicas converge on the same value,
but Client A's write is lost forever.
Server (key: "cart") Client A Client B
======================== ======== ========
v1: cart = {milk}
|
+--- read ----------------------> gets v1, {milk}
|
+--- read ----------------------------------------------------> gets v1, {milk}
|
+<-- write(v1, {milk, eggs}) ---- Client A adds eggs
|
v2: cart = {milk, eggs}
|
+<-- write(v1, {milk, bread}) --------------------------------- Client B adds bread
| (based on stale v1!)
v3: cart = [{milk, eggs},
{milk, bread}] <-- server keeps BOTH as siblings
|
+--- read ----------------------> gets v3, [{milk, eggs}, {milk, bread}]
|
+<-- write(v3, {milk, eggs, bread}) Client A merges siblings
|
v4: cart = {milk, eggs, bread} <-- conflict resolved, no data lost
(replica, version) pairs β for example {A:3, B:2, C:5} means replica A has seen 3 writes, B has seen 2, and C has seen 5.Replica A Replica B Replica C
========= ========= =========
Write X = 1
VA = {A:1}
|
+--- replicate -------> VB = {A:1}
| |
| Write X = 2
| VB = {A:1, B:1}
| |
| +--- replicate -------> VC = {A:1, B:1}
| |
Write X = 3 Write X = 4
VA = {A:2} VC = {A:1, B:1, C:1}
| |
+--- compare with C's vector --------------------------->|
| |
{A:2} vs {A:1, B:1, C:1} |
Neither dominates the other β writes are CONCURRENT |
System keeps both values as siblings |
| Aspect | Last Write Wins | Immutable Keys | Version Numbers | Version Vectors |
| Data loss | Yes β silent overwrites | None | None (siblings kept) | None (siblings kept) |
| Complexity | Very low | Low | Moderate | High |
| Clock dependency | Yes β needs timestamps | No | No | No |
| Multi-replica support | Yes | Yes | Single replica only | Yes β designed for it |
| Merge responsibility | System (automatic) | Application (at read) | Application (at read) | Application (at read) |
| Best suited for | Write-once data, caches | Event logs, audit trails | Single-leader with conflicts | Multi-leader / leaderless |
| Real-world usage | Cassandra, DynamoDB | Event sourcing systems | Single-node key stores | Riak, Dynamo-style DBs |