To start building your mental model, think of a CRDT as a container holding two essential components: the actual data to store and some metadata that helps in resolving conflicts.
The register type holds the actual data to be stored; each set of fields in a document or key‑value pair embedded in a map function as a single register object.
For example, the following snippet consists of three separate registers:
Since each set of fields in a document and, if embedded within a map, each key-value pair acts as a single register, you can structure, organize, and manage your data more efficiently.
You can encode simple data as well as highly complex datasets in a register:
- To encode simple data in a registers, use primitive JSON-compatible types, such as string, boolean, number, and so on.
- To encode more complex datasets in a register, use a combination of primitive types and register and maps.
The following table provides an overview of the types you can use to store a single value in a register:
An ordered list of values, where each value represents any primitive type, as well as nested collection types such as other arrays or maps
true or false
Embedded document object
Represents an absence of a value
Signed 64-bit integer
Unsigned 64-bit integer
Each register adheres to the last-writer-wins principle at merge, ensuring conflict-free replication and data consistency throughout the distributed system.
With last-writer-wins merge semantics, when conflicting updates occur, the update made by the last writer always takes precedence and propagates across peers as the single source of truth — the definitive value.
For example, one flight attendant updates a customer's seat number to 6 and another to seat 9. When two conflicting versions merge, the edit with the highest timestamp wins.
An extension to the register type, an array is an ordered collection of items represented using any primitive JSON‑compatible data type enclosed within square brackets ([ ]).
Avoid using arrays in Ditto.
Due to potential merge conflicts when offline peers reconnect to the mesh and attempt to sync their updates, especially when multiple peers make concurrent updates to the same item within the array.
CRDT-based arrays, when compared to typical arrays, have certain strengths and limitations.
While CRDTs excel at managing unordered operations, maintaining a strict order is less efficient, which could potentially lead to data inconsistencies at merge.
The reason for this is because, unlike traditional arrays, the multiple elements within a CRDT-based array function collectively as a single register, and, as previously explained in Ditto Basics, a register is effectively a container that holds multiple elements — the actual data and various metadata — just like regular arrays.
In simple terms, imagine an arraylike a string. Much like a string, each element in a CRDT-based array plays a unique role in determining the final value that ultimately forms; just like the string characters that eventually form a word.
Similar to how you access elements in a typical array, you reference each individual element in a CRDT-based array (a register) by their index. If those indexes change while disconnected from the network, once network conditions improve, they may fail to merge due to concurrency conflicts, resulting in data inconsistencies.
Considering this, avoid using arrays in Ditto and opt for a map instead, which effectively functions as a set, more commonly known as an associative array.
When managing data that requires unique identifiers and relationships, instead of using an array to encode your data, use a map with unique string keys and object values instead.
For example, instead of representing an array of cars, where each element represents a car:
Implement a map instead: