kuilt Help

ORMap

A map where keys can be added and removed, and values can be any CRDT. When two devices edit at the same time — one removes a key, another writes to it — the key survives (add-wins). The value at that key merges normally using its own type's rules.

Converges to: a map where key presence follows ORSet semantics (add-wins on conflict) and each value converges according to its own piece rule.

Merge rule

Key presence is an ORSet of presence dots. Value merging is the value type's own piece. When a key is removed by one replica and re-added with a new value by another concurrently, the ORSet semantics apply: the new add's dot survives, so the key is present, and the value is the merge of both sides.

Code examples

Put and query:

@Test fun putThenContains() { val m = ORMap.empty<String, GCounter>().put(a, "votes", GCounter.of(a to 1L)) assertTrue("votes" in m.keys) assertEquals(1L, m["votes"]?.value) }

Values merge via their own piece:

@Test fun valuesMergeViaTheirOwnPiece() { // Alice and Bob each insert their own per-replica GCounter under "votes"; merge sums them. val mA = ORMap.empty<String, GCounter>().put(a, "votes", GCounter.of(a to 3L)) val mB = ORMap.empty<String, GCounter>().put(b, "votes", GCounter.of(b to 5L)) val merged = mA.piece(mB) assertEquals(8L, merged["votes"]?.value) }

Add wins over concurrent remove:

@Test fun addWinsOverConcurrentRemove() { // shared start: alice puts "votes" -> {a:1} val start = ORMap.empty<String, GCounter>().put(a, "votes", GCounter.of(a to 1L)) val alice = start.remove("votes") // alice removes what she saw val bob = start.put(b, "votes", GCounter.of(b to 1L)) // bob concurrently re-puts val merged = alice.piece(bob) assertTrue("votes" in merged.keys) // add wins: bob's presence tag (B,1) survives // Bob's put pieced {a:1} with {b:1} — both GCounter slots survive (GCounter merge is max). // Alice's remove only tombstoned the presence tag (A,1), not the GCounter value. assertEquals(2L, merged["votes"]?.value) }

When to use

ORMap is the general-purpose CRDT map. It is a good fit when both key lifetime and value merging matter — for example, a map from player id to GCounter vote tally, where players can join and leave concurrently. For a simpler map where values are plain scalars and you want last-write-wins on each key, use LWWMap.

Last modified: 22 June 2026