CRDT: Multiple people can edit at the same time without anyone needing to mediate
Photo: Grokking the Tech Career - Substack

CRDT: Multiple people can edit at the same time without anyone needing to mediate

A data structure that allows multiple people to edit simultaneously, even when the network is down, and then merge their changes without the need for a mediator—explained using examples such as shopping lists, counters, and text editors.

Two people are working on a shopping list together on their phones. Both have lost their internet connection—one is in an elevator, and the other is on a plane. Lan adds "milk," and Minh adds "eggs." Half an hour later, both have their internet connection back.

The correct result should be a list containing both milk and eggs. But for the machine to do that on its own without asking anyone, it requires a data design approach that’s completely different from the usual one. That’s CRDT.

Why Does the Familiar Method Cause Data Loss?

The most common approach is to let the server decide: whichever version is submitted later wins. That sounds reasonable, but let’s see what happens with the shopping list.

Ghi đè theo thời gian làm mất hẳn phần việc của người gửi trước
Overwriting over time completely erases the work done by the previous sender

Lan's file was uploaded first, and Minh's file was uploaded later. The server saved Minh's version and deleted Lan's. The "edit" option disappeared, and no one was notified.

The second approach is to ask the user, "There's a conflict—which version do you want to keep?" It's safer, but it shifts the burden to the user, and with items that are constantly being updated, like shared documents, you'd be asking this all the time.

CRDT takes a third approach: designing data structures so that aggregation always yields a single, correct answer that can be computed by a machine, without the need for a referee or consultation with anyone.

The Three Qualities That Make Magic Happen

To merge without arbitration, the merger must satisfy three conditions. It sounds very mathematical, but the practical implications are simple.

Ba tính chất bắt buộc của phép gộp trong CRDT
The Three Mandatory Properties of Composition in CRDTs

Swapping the order—that is, merging Lan’s version into Minh’s, or vice versa—yields the same result. This is important because on the network, you have no control over which packet arrives first.

Grouping means that with three or more machines, it doesn’t matter which pairs are grouped first. There’s no need for a global order—something that’s very difficult to achieve in a distributed system.

"Harmless duplication" means that receiving the same packet twice is not an error. This saves you from an entire class of errors, since real networks always send duplicate packets when retrying.

Given these three properties, the copies will certainly converge to the same state—regardless of who sent them first, which packets got lost, or which ones arrived twice.

The simplest example: counting likes

Suppose a post is liked on three different servers, and they synchronize slowly. If each server keeps its own count and then takes the largest number when merging them, you’ll lose likes. If you simply add them up without checking, you’ll count them twice.

The solution is that each machine may only write in its own cell.

Bộ đếm chỉ tăng: mỗi máy một ô riêng, gộp là lấy giá trị lớn nhất từng ô rồi cộng
The counter only increments: each machine has its own cell; to combine them, take the largest value from each cell and add them together

Machine A counts 5, Machine B counts 3, and Machine C counts 2. To combine the results, we take the largest value from each cell and add them together: 5 + 3 + 2 = 10.

Why take the largest value instead of adding it? Because the cell for Machine A is written to only by Machine A, so the number there always increases. If an older version is received, ignore it; if a newer version is received, update it. It doesn’t matter if a duplicate is received—taking the largest value twice still yields the same number.

To count both increases and decreases, use two counters as described above—one for increases and one for decreases—and the result is the difference between the two totals.

Sets: Adding is easy; deleting is the hard part

Back to the shopping list. Adding an item is easy: union is the operation that combines two sets, and the union satisfies all three properties listed above.

The trouble started when something was deleted.

Không lưu dấu vết xoá thì phần tử đã xoá sẽ sống lại sau khi đồng bộ
If you don't mark the deletion, the deleted element will be restored after synchronization.

Lan deleted "milk" from the list. Minh's computer didn't know about that, so "milk" was still there. When they merged the lists using the union operation, "milk" reappeared—because to a computer, "I don't have milk" and "I deleted milk" look exactly the same.

The solution is to keep a record of changes: when an element is deleted, we don’t remove it but mark it as deleted. This mark is often called a “tombstone.” During a merge, the deletion mark takes precedence over the surviving element, so the result is what the user intends.

The cost is that the list of tombstones just keeps getting longer. A document that has been edited over many years may carry more traces than actual content. The system must be cleaned up periodically, and this can only be done once it is certain that all copies have received notification of the deletion.

Text: The Most Difficult Problem

Collaborative editing is the aspect of CRDT for which it is best known, and it is also the most challenging. The problem is that the position within the text is not fixed.

Số thứ tự vị trí bị xê dịch khi người khác chèn; định danh bền vững thì không
The position number shifts when someone else inserts content; the persistent identifier does not.

Suppose the text is currently "MEO." Lan inserts "C" at the beginning, and Minh inserts "O" at the end. If we describe the operation using a position number—"insert at position 3"—then after Lan’s operation is applied, position 3 now points to a different location. Minh’s character ends up in the wrong place.

The solution is to eliminate sequence numbers entirely. Each character is assigned a permanent identifier that never changes, and the insertion operation is described in terms of the relationship: "between the character with code X and the character with code Y."

The interesting detail here is that between any two codes, there is always a new code that lies between them—just as between any two real numbers, there is always another real number. Thanks to this, there is always room to insert a new code, no matter how many times you insert one at the same point.

When two people try to insert data into the exact same slot, a tie-breaking rule is needed—usually a comparison of the machine’s identifier. Any rule will do, as long as all machines use the same rule, because the goal is for everyone to arrive at the same result.

Result: All copies match

Gộp theo thứ tự nào thì các bản sao cũng về cùng một trạng thái
No matter the order in which they are merged, the copies will all end up in the same state

This is what CRDT promises and delivers: machines can receive changes in any order, as many times as necessary, as long as everyone eventually receives all the changes, everyone will end up with the same state. No central server is needed, no keys are required, and there’s no need to ask the user.

Two Approaches to Implementation

Send status. Each machine periodically sends its entire status, and the recipient aggregates it. This method is simple and tolerant of packet loss, but it consumes a lot of bandwidth if the data is large. Modern implementations send only the changes instead of the entire status.

Send the operation. Each machine sends only the operation it just performed. This is very efficient, but requires the transport layer to ensure that all operations arrive, are not lost, and are not applied more than once.

What CRDT Doesn't Promise

This is the most common source of misunderstanding. CRDTs ensure that all copies converge to the same result. They do not guarantee that the result will be to the user’s liking.

If two people edit the same sentence in different directions, the result could be a nonsensical hybrid sentence. CRDT only guarantees that everyone will see that very hybrid sentence—it doesn’t guarantee that it’s any good.

CRDTs cannot handle operations that require global consistency—such as "the balance must not be negative" or "only one person can hold this position." Those cases still require true consensus.

When to Use It

  • Suitable for: notes, to-do lists, drawings, collaborative drafting, and multi-device sync—where internet outages are common and auto-merge is better than forcing users to choose
  • Not suitable: financial transactions, reservations, inventory — where an incorrect result is truly wrong, not just unsightly
  • Consider: very large datasets or data that’s continuously updated over many years, as the accompanying metadata can exceed the actual data

If you're planning to implement this yourself, use an existing library instead of writing it from scratch. The hard part isn't the idea—the idea is just like the one in this article—but rather compressing metadata, cleaning up orphaned entries, and maintaining performance as the document grows.

Chia sẻ

Thảo luận