lattice_maps/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_maps/crdt
import lattice_core/replica_id
import lattice_counters/g_counter
import lattice_maps/or_map
let map = or_map.new(replica_id.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 opaque type ORMap
Values
pub fn from_json(
json_string: String,
) -> Result(ORMap, json.DecodeError)
Decode an ORMap from a JSON string produced by to_json.
Supports both v1 (no remove_bounds) and v2 (with remove_bounds) formats. v1 maps are decoded with empty remove_bounds, meaning no value compaction is possible until new removes are performed.
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,
) -> Result(ORMap, crdt.MergeError)
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.
Returns Error(TypeMismatch(...)) if the two maps have different
crdt_spec values (e.g., one holds counters and the other holds sets).
pub fn new(
replica_id: replica_id.ReplicaId,
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 prune(
map: ORMap,
stable_vv: version_vector.VersionVector,
) -> ORMap
Prune tombstones for keys and compact removed values whose removal is causally stable.
Delegates to or_set.prune to remove tombstones from the internal key
tracker. Then, for each removed key that has a recorded causal bound, if
the pruned version vector dominates that bound, the key’s CRDT value is
discarded (the removal is stable and no concurrent re-add can reference
the old value).
Only call this with a version vector representing events that have been seen by all replicas (causally stable), otherwise zombie updates might be incorrectly ignored.
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 until prune determines the removal is causally stable. A causal bound is recorded so prune can later decide when it is safe to discard the value.
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": 2, "state": {"replica_id": "...", "crdt_spec": "...", "key_set": "...", "values": [...], "remove_bounds": {...}}}
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.