Multi-Master Replication Conflict Resolution: LWW vs. CRDTs
Master multi-master replication conflict resolution. Learn the trade-offs between Last-Write-Wins and CRDTs to build more resilient distributed systems.
When you're scaling a system globally, you’ll eventually hit the wall of multi-master replication. I remember sitting in a war room three years ago, staring at a dashboard where our user profiles were flickering between two different states because of concurrent writes from different regions. We had ignored the reality of network partitions, and our database was paying the price.
If you’re prepping for a system design interview or architecting a production system, you need to understand that there is no "perfect" way to handle concurrent edits. You’re always trading off complexity, consistency, and user experience.
Understanding Multi-Master Replication Challenges
In a multi-master replication setup, every node is a primary. You can write to any node, and the system eventually propagates those changes to the others. The problem arises when two nodes accept a write for the same record at roughly the same time. Because of the CAP theorem, you can't have perfect consistency and high availability during a partition. You have to pick a resolution strategy.
The "Easy" Way: Last-Write-Wins (LWW)
Last-Write-Wins is exactly what it sounds like: the system keeps the value with the latest timestamp. It’s simple to implement and requires zero application-level logic.
However, LWW is dangerous. Clock skew is real. Even with NTP (Network Time Protocol), servers drift. If Node A is 50ms ahead of Node B, an older write from Node A could overwrite a newer, more important write from Node B. You lose data silently, and the user never knows why their update "vanished."
The Robust Way: CRDTs
Conflict-free Replicated Data Types (CRDTs) are the industry standard for collaborative applications like Google Docs or local-first databases. Instead of picking a winner, CRDTs merge the state.
A simple example is a G-Counter (Grow-only Counter). Instead of storing a single integer, each node maintains its own count. When you need the total, you sum the counts from all nodes. It’s mathematically guaranteed to converge to the same value across all replicas.
| Strategy | Complexity | Data Loss | Best Use Case |
|---|---|---|---|
| LWW | Low | High (Silent) | Simple caches, low-stakes UI state |
| CRDTs | High | None | Collaborative editing, distributed counters |
| Semantic | Medium | Low | Domain-specific rules (e.g., shopping carts) |
Implementing Conflict Resolution
When I moved our team toward a more resilient architecture, we realized that we didn't need CRDTs for everything. For our user profile service, we actually used a hybrid approach. We used LWW for simple fields like "display name" but implemented a custom semantic merge for "permissions" to ensure we never accidentally revoked access.
If you are building a Laravel SaaS MVP, you might not need to roll your own CRDT library. Often, the best strategy is to avoid the conflict entirely.
Flow diagram: Client Write → Node A; Node A → Local Update; Local Update → Propagate to B; Propagate to B → Conflict?; E -- Yes → Apply Strategy; E -- No → Commit; Apply Strategy → LWW or Merge
Why I Prefer Semantic Merging
Before you jump to the complexity of CRDTs, ask yourself: can I resolve this at the application layer?
Semantic merging means defining rules for your data. If two users add items to a shopping cart, the "merge" isn't picking one cart over the other—it's taking the union of both lists. This is technically a form of CRDT, but it’s tailored to your business logic. It feels more natural to the user than having their cart suddenly revert to an older state because of a timestamp collision.
The Reality Check
We’ve all been there: you implement a complex system, and it works great until a weird edge case pops up. In distributed systems, that edge case is a certainty.
If I were to rebuild our replication layer today, I would focus less on the "perfect" algorithm and more on observability. You need to know when a conflict occurs. Don't just resolve it silently; log it. When you're managing database replication strategies, visibility is your best defense against silent data corruption.
I’m still not convinced that CRDTs are necessary for every distributed CRUD app. They add overhead to your object size and require careful implementation. Sometimes, a simple "last writer wins" with a properly synchronized timestamp service is enough for non-critical systems. The key is knowing which data is valuable enough to warrant the complexity of a merge-based system and which can handle the occasional silent overwrite.
FAQ
Q: Is Last-Write-Wins ever okay? A: Yes. If your data is idempotent or the loss of an update is trivial (like a "last active" timestamp), LWW is significantly cheaper and easier to maintain.
Q: Are CRDTs slow? A: They can be. As the history of operations grows, the state size increases. You often need "garbage collection" or state compaction to keep performance acceptable.
Q: What is the biggest mistake in multi-master setups? A: Relying on application-level logic to handle conflicts that should be handled at the database level, or vice-versa. Always define your consistency requirements before picking your tool.
What I’d do differently next time? I’d start with a single-master setup using a transactional outbox for as long as possible. Multi-master is a powerful tool, but it's a "last resort" for scaling, not a starting point.