lattice/or_map
An observed-remove map (OR-Map) CRDT.
Keys are tracked using an OR-Set with add-wins semantics: concurrent update
and remove of the same key resolves in favor of the update. Each value is
itself a CRDT (specified by CrdtSpec at construction), enabling nested
convergent data structures.
Example
import lattice/crdt
import lattice/g_counter
import lattice/or_map
let map = or_map.new("node-a", crdt.GCounterSpec)
|> or_map.update("score", fn(c) {
let assert crdt.CrdtGCounter(gc) = c
crdt.CrdtGCounter(g_counter.increment(gc, 10))
})
Types
An OR-Map (observed-remove map) CRDT.
Keys are tracked using an ORSet(String) which provides add-wins
semantics: if an update and a remove of the same key happen concurrently
on different replicas, the update wins after merging.
Values are stored as the Crdt tagged union so they can be merged
per-key using type-specific logic. The crdt_spec field records which
CRDT type is used for values, enabling auto-creation of default values
for new keys.
pub type ORMap {
ORMap(
replica_id: String,
crdt_spec: crdt.CrdtSpec,
key_set: or_set.ORSet(String),
values: dict.Dict(String, crdt.Crdt),
)
}
Constructors
-
ORMap( replica_id: String, crdt_spec: crdt.CrdtSpec, key_set: or_set.ORSet(String), values: dict.Dict(String, crdt.Crdt), )
Values
pub fn from_json(
json_string: String,
) -> Result(ORMap, json.DecodeError)
Decode an ORMap from a JSON string produced by to_json.
Returns Error if the string is not valid JSON, does not match the
expected format, or contains an unknown crdt_spec string.
pub fn get(map: ORMap, key: String) -> Result(crdt.Crdt, Nil)
Get the CRDT value at key.
Returns Ok(crdt) if the key is active in the OR-Set.
Returns Error(Nil) if the key has never been added, or has been removed
and not re-added.
pub fn keys(map: ORMap) -> List(String)
Return the list of all active keys (those present in the OR-Set).
Order is not guaranteed.
pub fn merge(a: ORMap, b: ORMap) -> ORMap
Merge two OR-Maps.
The OR-Set key trackers are merged with add-wins semantics: if a key was
concurrently updated on one replica and removed on another, the key
survives in the merged result. CRDT values are merged per-key using
crdt.merge for type-specific convergence.
Merge is commutative, associative, and idempotent (a valid CRDT join).
pub fn new(replica_id: String, crdt_spec: crdt.CrdtSpec) -> ORMap
Create a new empty OR-Map for the given replica with the specified CRDT type.
The crdt_spec determines what type of CRDT is auto-created when update
is called on a key that does not yet exist in the map.
pub fn remove(map: ORMap, key: String) -> ORMap
Remove a key from the OR-Map.
Removes the key from the OR-Set (marking it inactive). The underlying CRDT value is retained in the values dict so it can participate in per-key merge if the key is concurrently re-added on another replica.
pub fn to_json(map: ORMap) -> json.Json
Encode an ORMap as a self-describing JSON value.
The nested OR-Set (key_set) and CRDT values are double-encoded as JSON
strings so they can be decoded using the existing from_json APIs.
Format: {"type": "or_map", "v": 1, "state": {"replica_id": "...", "crdt_spec": "...", "key_set": "...", "values": [...]}}
The encoded value can be restored with from_json.
pub fn update(
map: ORMap,
key: String,
f: fn(crdt.Crdt) -> crdt.Crdt,
) -> ORMap
Apply a function to the CRDT value at key, auto-creating it if absent.
If the key does not exist, a default value is created from crdt_spec
and passed to f. The key is added to the OR-Set, marking it active.
The return value of f replaces (or sets) the value for that key.