Collection Types
DSM exposes three collection models. Choose based on the coordination problem, not by habit.
Quick Mental Model
REGISTER
one key -> one replicated value
best when metadata ordering is enough
LEASE
one key -> one active owner at a time
best when fencing and ownership matter
CRDT
many nodes -> local updates -> mergeable state
best when convergence matters more than single-writer orderSide-By-Side View
+----------------+----------------------+-------------------------------+
| model | write pattern | use when |
+----------------+----------------------+-------------------------------+
| register | one value per key | metadata and routing hints |
| lease | one active owner | fencing and ownership |
| crdt | concurrent updates | counters and mergeable state |
+----------------+----------------------+-------------------------------+Register Collections
Use registers when you need replicated metadata keyed by an entry ID.
Typical workloads:
- route hints
- service-local feature state
- cluster-visible operational metadata
Register collections are created with CollectionSpecBuilder.register(...).
CollectionSpecBuilder.<RouteHint>register(
"shared",
"gateway",
"route-hints")
.schemaId("route-hints/v1")
.codec(new RouteHintCodec())
.build();Default builder behavior:
- consistency tier:
REGISTER - replication profile:
embeddedRegister() - QoS profile:
bestEffortMeta() - persistence profile:
ephemeral()
Think of a register as: "I want the cluster to agree on the latest metadata value for this key."
register example
key = edge-eu-west-1
value = 10.1.0.8:8443
node-a put(route-hint)
-> delta replicates
-> peers now resolve edge-eu-west-1 the same wayLease Collections
Use leases when a single owner must hold a responsibility at a point in time.
Typical workloads:
- shard ownership
- leader election
- ownership handoff with fencing tokens
Lease collections are created with LeaseCollectionSpecBuilder.lease(...). They add lease term, renewal skew, expiry grace, and entity factory options.
LeaseCollectionSpecBuilder.<ShardOwner>lease(
"shared",
"worker",
"shard-owner")
.schemaId("shard-owner/v1")
.codec(new ShardOwnerCodec())
.entityFactory(ShardOwner::blank)
.build();Default builder behavior:
- replication profile:
embeddedLease() - QoS profile:
controlCritical() - persistence profile:
localDurable() - lease mode:
AUTONOMOUS - lease term:
10s - renew skew:
3s - expiry grace:
500ms
ASCII timeline:
worker-a acquires shard-17
|
| lease term 10s
| renew before final 3s window closes
v
[ owned ] ---- renew ---- renew ---- renew ---->
if renew stops:
wait until term expires
apply expiry grace
allow another owner to acquirelease state intuition
FREE -> ACQUIRED -> RENEWING -> EXPIRED -> FREE
\-> TRANSFERRED -> ACQUIRED by another nodeUse a lease when "who owns this key right now?" is the core question.
CRDT Collections
Use CRDTs when every node should be able to update locally and the cluster should converge through a merge function.
Typical workloads:
- counters
- monotonic aggregates
- mergeable control-plane views
CRDT collections are created with CrdtCollectionSpecBuilder.crdt(...). They require an update codec, state codec, initial state, and merger.
CrdtCollectionSpecBuilder.<PnCounterUpdate, PnCounterState>crdt(
"shared",
"worker",
"request-counter")
.schemaId("request-counter/v1")
.codec(new PnCounterUpdateCodec())
.stateCodec(new PnCounterStateCodec())
.initialState(PnCounterState.empty())
.build();Default builder behavior:
- replication profile:
embeddedCrdt() - QoS profile:
standard() - persistence profile:
localDurable()
ASCII convergence picture:
node-a: +3 updates --->
\
> merge -> shared state = 8
/
node-b: +5 updates --->crdt intuition
node-a local state = 3
node-b local state = 5
after merge
cluster-visible state = 8Use a CRDT when local writes on multiple nodes should merge instead of fighting for one winning write.
How To Choose
Use this rule of thumb:
Need the latest metadata value? -> register
Need one active owner with fencing? -> lease
Need concurrent local updates? -> CRDTIf you are unsure, start with a register. Move to a lease only when ownership matters, and move to a CRDT only when multiple nodes truly need local writes.
Default Operational Profiles
- Registers default to embedded register replication, best-effort metadata QoS, and ephemeral persistence.
- Leases default to embedded lease replication, control-critical QoS, and local durable persistence.
- CRDTs default to embedded CRDT replication, standard QoS, and local durable persistence.