ORMap
An observed-remove map: keys K each carry a Quilted value S that merges via its own Quilted.piece. A concurrent put of the same key survives a remove (add-wins on the key), and a key's value is every surviving write to it, pieced together.
Built over Causal<DotMap<K, ORMapEntry<S>>>: an entry maps each of the key's presence tags to the value written under it, and the key's value is the join of the tags that are still live. A remove retires the tags it can see, so it takes their contributions with it; a write it never observed keeps both its tag and its value. That correspondence between a contribution and its tag is what makes piece associative — see ORMapEntry for why an entry-level value cannot be (#2086).
Every mutator returns the change rather than a new map: put and remove hand back a Patch holding just the key they touched, which is what belongs on the wire. piece absorbs one — and is also how a caller who wants the resulting whole map gets one: map.piece(map.put(replica, key, value)).
Serialization. An entry keys its contributions by Dot, so plain JSON needs Json { allowStructuredMapKeys = true } — the same flag MVRegister and ResettableCounter already require. CBOR and Protobuf encode it without any flag.
Samples
val a = ReplicaId("A")
val b = ReplicaId("B")
// Two peers have converged: "team" already holds a long roster, put there by B.
var alpha = ORMap.empty<String, GSet<String>>()
.piece { it.put(b, "team", GSet.of("alice", "bob", "carol", "dan")) }
var bravo = alpha
// A adds one member and puts only the change on the wire. The delta carries A's one name —
// not the merged roster — because the receiver re-does that merge against its own copy.
val hire = alpha.put(a, "team", GSet.of("erin"))
check(hire.delta["team"] == GSet.of("erin"))
// A's tag joins B's rather than replacing it, so the key's value is both writes together.
alpha = alpha.piece(hire)
bravo = bravo.piece(hire)
check(alpha == bravo)
check(alpha["team"] == GSet.of("alice", "bob", "carol", "dan", "erin"))
// A concurrent put beats a concurrent remove: B's put mints a tag A's remove never saw.
val concurrent = alpha.put(b, "team", GSet.of("frank"))
check("team" in alpha.piece(alpha.remove("team")).piece(concurrent).keys)
// A remove lands everywhere, because both peers agree on which tags are live. Had A already
// held a tag on "team" and the delta kept quiet about it, that older tag would still be alive
// on bravo — and "team" would come back from the dead there.
val disband = alpha.remove("team")
alpha = alpha.piece(disband)
bravo = bravo.piece(disband)
check("team" !in alpha.keys)
check("team" !in bravo.keys)Functions
The causal Dots this state has delivered — (author, author-seq) per op.
The per-author high-water of dots this state delivered and has since compacted away without retaining their identities.