Active-active and resolving write conflicts
Running two regions that both accept writes doubles your availability and serves users locally — but the moment both edit the same key, you have a conflict to resolve.
Active-passive vs active-active
When you replicate across regions, who serves writes?
- Active-passive — one region takes all writes; the other is a warm standby that takes over on failover. Simple and conflict-free, but you pay for an idle standby, far-away users write across an ocean, and failover has a gap.
- Active-active — both regions accept reads and writes, each serving its local users with low latency and absorbing the other’s load if one dies. You use all your capacity — but now two places can change the same data at once.
Active-active buys availability and locality; the bill is write conflicts.
Why conflicts happen
Cross-region replication is asynchronous — it has to be, or every write would pay a transoceanic round trip. So there’s a window where region A has accepted a write that region B hasn’t seen yet. If a user in B writes the same key in that window, both regions accepted a write to it independently, and when they sync they disagree:
t0 A: set price = 10 ───async replicate──┐
t0 B: set price = 12 ───async replicate──┤ (each accepted locally)
t1 A has 10, B has 12 → which one wins?
There’s no global lock to have prevented it — that’s the whole point of taking writes in both places.
Resolution strategies
- Last-write-wins (LWW) — attach a timestamp, keep the latest. Dead simple and what many stores default to, but it silently discards the losing write, and clock skew between regions means “latest” can be wrong. Fine for caches and presence; dangerous for anything you can’t lose.
- Version vectors / detect-and-surface — track causality so the system can tell a true concurrent conflict from a stale overwrite, then keep both versions and let the application or user merge (the way a shopping cart or a source-control merge works).
- CRDTs — conflict-free replicated data types (counters, sets, registers, the sequences behind collaborative text) are designed so concurrent updates merge deterministically to the same result regardless of order — no coordination, no lost writes, for the operations they support.
- Avoid the conflict — partition writes so a given key is only ever written in one region (a “home region” per user/tenant). No concurrent writes to the same key means no conflict to resolve — often the most pragmatic choice.
The trade-off in one line
Active-active maximizes availability and locality; conflict resolution is the price, and every strategy trades simplicity (LWW, lose data) against correctness (version vectors/CRDTs/home-region, keep data).
It’s CAP/PACELC in the concrete: leaderless multi-region leans AP and pushes consistency work into conflict handling.
Where it shows up
DynamoDB global tables (LWW by default), Cassandra multi-DC, Cosmos DB multi-region writes with selectable conflict policies, and collaborative apps (Google Docs, Figma) built on CRDTs/OT.
The interview cue
When a design wants multi-region writes, name the consequence: “Active-active gives me local low-latency writes and full use of both regions, but replication is async so two regions can write the same key concurrently. I’d resolve with version vectors or CRDTs where correctness matters — LWW silently drops a write — or sidestep it entirely by giving each key a home region.” Choosing active-active and stating how conflicts resolve is the senior move.