Collection Specs Reference
This page documents the builders used to declare DSM collections.
Collection Identity
All three collection builders start with the same locator parts:
tenantIdapplicationIdcollectionId
Together they form the collection locator. All nodes that should participate in the same logical collection must use the same locator.
Register Collections
Use CollectionSpecBuilder.register(tenantId, applicationId, collectionId) for register-style collections.
Required Fields
schemaId(String)codec(DsmEntityCodec<E>)
Optional Fields
consistencyTier(ConsistencyTier)replicationProfile(ReplicationProfile)qosProfile(QosProfile)persistenceProfile(PersistenceProfile)conflictResolver(ConflictResolver<E>)evictionPolicy(EvictionPolicy<E>)
Defaults
- consistency tier:
REGISTER - replication profile: embedded register
- QoS profile: best-effort metadata
- persistence profile: ephemeral
Example
CollectionSpec<RouteHint> spec = CollectionSpecBuilder.<RouteHint>register(
"shared",
"gateway",
"route-hints")
.schemaId("route-hints/v1")
.codec(new RouteHintCodec())
.build();Lease Collections
Use LeaseCollectionSpecBuilder.lease(tenantId, applicationId, collectionId) for ownership-sensitive collections.
Required Fields
schemaId(String)codec(DsmEntityCodec<E>)entityFactory(Function<String, E>)
Optional Fields
replicationProfile(ReplicationProfile)qosProfile(QosProfile)persistenceProfile(PersistenceProfile)leaseMode(LeaseMode)leaseTerm(Duration)renewSkew(Duration)expiryGrace(Duration)
Defaults
- replication profile: embedded lease
- QoS profile: control critical
- persistence profile: local durable
- lease mode:
AUTONOMOUS - lease term:
10s - renew skew:
3s - expiry grace:
500ms
Example
LeaseCollectionSpec<ShardOwner> spec = LeaseCollectionSpecBuilder.<ShardOwner>lease(
"shared",
"worker",
"shard-owner")
.schemaId("shard-owner/v1")
.codec(new ShardOwnerCodec())
.entityFactory(ShardOwner::blank)
.leaseTerm(Duration.ofSeconds(10))
.renewSkew(Duration.ofSeconds(3))
.build();Practical Constraints
- the entity factory must be able to create a new blank entity from an entry key
renewSkewshould stay smaller thanleaseTerm- persistence defaults to local durable because lease state is operationally important
LeaseMode.QUORUMrejects local acquire, renew, and transfer while membership is unstable or majority visibility is unavailable
CRDT Collections
Use CrdtCollectionSpecBuilder.crdt(tenantId, applicationId, collectionId) for mergeable state.
Required Fields
schemaId(String)codec(DsmEntityCodec<E>)stateCodec(MergeableStateCodec<S>)initialState(S)
Optional Fields
replicationProfile(ReplicationProfile)qosProfile(QosProfile)persistenceProfile(PersistenceProfile)
Defaults
- replication profile: embedded CRDT
- QoS profile: standard
- persistence profile: local durable
Example
CrdtCollectionSpec<PnCounterUpdate, PnCounterState> spec =
CrdtCollectionSpecBuilder.<PnCounterUpdate, PnCounterState>crdt(
"shared",
"billing",
"request-counter")
.schemaId("request-counter/v1")
.codec(new PnCounterUpdateCodec())
.stateCodec(new PnCounterStateCodec())
.initialState(PnCounterState.empty())
.build();The runtime registration call also requires a StateMerger<E, S>.
Profiles
The builders expose three profile dimensions:
Replication Profile
Controls how the collection is replicated by the embedded runtime sync path.
Built-in names used by the Spring path are:
embedded-registerembedded-leaseembedded-crdt
QoS Profile
Controls message delivery characteristics and intended priority.
Built-in names used by the Spring path are:
best-effort-metacontrol-criticalstandard
Persistence Profile
Controls local durability behavior.
Built-in names used by the Spring path are:
ephemerallocal-durable
Schema IDs
schemaId is part of your compatibility contract. Recommended practice:
- change the schema ID when the payload shape or semantics are not safely compatible
- keep the format human-readable, such as
route-hints/v1
For record entities, RecordCodec also includes an 8-byte schema fingerprint in serialized payloads. A fingerprint mismatch is reported as DSM-6003 and the incompatible remote register upsert is ignored locally.
Conflict Resolution
Register specs can attach a custom ConflictResolver<E>. A resolver must be commutative: resolving (A, B) and (B, A) must produce the same semantic winner. Wrap new custom resolvers with ConflictResolver.verified(delegate) in tests or staging to catch non-commutative behavior.
When a resolver returns a merged entity, the entity must carry valid lineage metadata. A merged entity with zero physical time and zero logical counter is rejected as DSM-3005.
Eviction Policies
Register specs can attach an EvictionPolicy<E>. The default is no eviction. The built-in TTL policy evicts entities whose explicit expiresAt has passed, or whose metadata physical timestamp plus TTL has expired.
The default in-memory runtime materializes eviction as a normal remove from the local active store, so change streams and replication behavior observe the removal path.
Which Builder To Start With
- use register builders first unless you have a clear need for lease or CRDT semantics
- use lease builders for fenced ownership workflows
- use CRDT builders for multi-writer convergent state