Shard Owner Election
This cookbook shows how to model single-owner work distribution with a lease collection.
When This Pattern Fits
Use this pattern when:
- exactly one node should actively own a key at a time
- ownership must be renewed or it expires
- you need fencing-aware transfer or verification
- another node must be able to take over after expiry
Typical examples:
- shard ownership
- active worker selection
- lease-based leader election per partition
Recommended DSM Shape
- collection type:
LEASE - locator example:
shared/worker/shard-owner - entity type: immutable
LeaseEntity<E>record - persistence default:
local-durable
Mental Model
text
shard-17 ownership
worker-a acquire(shard-17)
|
+--> success, fencing token = 42
|
+--> renew before lease term closes
|
+--> keep processing shard-17
if worker-a stops renewing:
|
+--> lease term expires
+--> expiry grace elapses
+--> worker-b may acquire shard-17Lease Timeline
text
time ------------------------------------------------------------>
acquire renew window begins expiry
| | |
v v v
[ owned -----------------+------------- ][ expired ][ free ]
<------ term --------->
< renew-skew >Entity Model
java
public record ShardOwner(
String entryKey,
EntityMetadata metadata,
LeaseState leaseState)
implements LeaseEntity<ShardOwner> {
public static ShardOwner blank(String entryKey) {
return ...;
}
@Override
public ShardOwner withMetadata(EntityMetadata metadata) {
return new ShardOwner(entryKey, metadata, leaseState);
}
@Override
public ShardOwner withLeaseState(LeaseState state) {
return new ShardOwner(entryKey, metadata, state);
}
}Registration Pattern
java
DsmLeaseRegister<ShardOwner> shardOwners = runtime.leaseRegister(
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))
.expiryGrace(Duration.ofMillis(500))
.build());Acquisition Pattern
java
LeaseAcquireResult<ShardOwner> acquired = shardOwners.acquire(
"shard-17",
LeaseAcquireOptions.defaults())
.join();Renewal Pattern
java
LeaseRenewResult<ShardOwner> renewed = shardOwners.renew(
"shard-17",
LeaseRenewOptions.defaults())
.join();Handoff Pattern
text
worker-a owns shard-17
|
+--> transfer(shard-17, worker-b)
|
+--> worker-b becomes active owner
|
+--> old fencing token must no longer be trustedDesign Rules
- use one lease key per owned work item
- keep renewal scheduling comfortably inside the renew-skew window
- treat fencing tokens as part of the correctness contract for downstream side effects
- do not use lease state as a generic metadata bag
Common Mistakes
Renewing Too Late
If renewal is scheduled too close to expiry, transient pauses can cause avoidable ownership churn.
Ignoring Fencing
Lease ownership is not just "latest writer wins". Downstream processing should respect the current lease lineage and fencing decision.
Using A Lease For Mergeable Metrics
If multiple nodes should contribute to a shared value, use a CRDT instead.
Spring Boot Shape
yaml
dsm:
collections:
- bean-name: shardOwnersCollection
tenant-id: shared
application-id: worker
collection-id: shard-owner
schema-id: shard-owner/v1
type: LEASE
consistency-tier: LEASE
codec-bean: shardOwnerCodec
lease:
mode: AUTONOMOUS
term: 10s
renew-skew: 3s
expiry-grace: 500ms
entity-factory-bean: shardOwnerEntityFactory