I thought concurrent writes would somehow merge automatically. They don’t. When two nodes accept writes to the same record simultaneously, the database doesn’t magically resolve it. You have to.

Avoiding Conflicts#

Easiest solution: don’t allow them.

Single leader replication. All writes to a key go through one node. No concurrent writes possible. This is what most systems do because conflict resolution is hard.

If you must use multi-leader, partition carefully. User A’s data always writes to datacenter 1, User B to datacenter 2. Conflicts only happen if you mess up the partitioning.

Detecting Conflicts#

Sometimes conflicts happen anyway. How do you know?

Timestamps: Each write gets a timestamp. If two writes have different timestamps for the same key, there’s a conflict. Simple, but clocks lie. Datacenter A’s clock runs 200ms fast, its writes always “win” even when they happened second.

Version vectors: Each replica tracks a counter for every other replica. When you see writes with version vectors like {A:1, B:0} and {A:0, B:1}, neither dominates the other. That’s a conflict.

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#000000','primaryTextColor':'#00ff00','primaryBorderColor':'#00ff00','lineColor':'#00ff00','secondaryColor':'#000000','tertiaryColor':'#000000','noteBkgColor':'#000000','noteBorderColor':'#00ff00','noteTextColor':'#00ff00'}}}%% sequenceDiagram autonumber participant U1 as User A participant R1 as Replica 1 participant R2 as Replica 2 participant U2 as User B U1->>R1: Write: status="pending" Note over R1: {R1:1, R2:0} U2->>R2: Write: status="completed" Note over R2: {R1:0, R2:1} R1-->>R2: Sync R2-->>R1: Sync Note over R1,R2: Conflict detected
Neither vector dominates

Version vectors tell you a conflict exists but don’t solve it.

Resolving Conflicts#

Last-write-wins (LWW): Pick the write with the latest timestamp, discard the rest. Simple, but silently loses data when clocks drift.

Keep both, merge later: Store all conflicting versions. Let the application decide. Shopping carts can merge by taking the union of items. Most data isn’t that forgiving.

CRDTs: Data structures designed so merges are automatic. Counter increments always sum cleanly regardless of order. Collaborative editors use them to handle concurrent edits. I get the concept for specific cases like that, just haven’t needed them in the systems I’ve built.

I’m still figuring out when you’d actually use multi-leader in production. The conflict resolution complexity seems brutal. Single leader with async replicas handles most cases I’ve seen.

Have you worked with multi-leader replication? How’d you handle conflicts?