0
# Allocation Strategies
1
2
Shard allocation strategies control how shards are distributed across cluster nodes and when rebalancing occurs. Akka Cluster Sharding provides built-in strategies and allows custom implementations.
3
4
## Core Strategy Interface
5
6
### ShardAllocationStrategy Trait
7
8
```scala { .api }
9
trait ShardAllocationStrategy extends NoSerializationVerificationNeeded {
10
def allocateShard(
11
requester: ActorRef,
12
shardId: ShardId,
13
currentShardAllocations: Map[ActorRef, immutable.IndexedSeq[ShardId]]
14
): Future[ActorRef]
15
16
def rebalance(
17
currentShardAllocations: Map[ActorRef, immutable.IndexedSeq[ShardId]],
18
rebalanceInProgress: Set[ShardId]
19
): Future[Set[ShardId]]
20
}
21
```
22
23
**Key Methods:**
24
- **`allocateShard`**: Decides where to place a new shard when first accessed
25
- **`rebalance`**: Periodically called to determine which shards should be moved
26
27
### Startable Allocation Strategy
28
29
For strategies that need initialization:
30
31
```scala { .api }
32
trait StartableAllocationStrategy extends ShardAllocationStrategy {
33
def start(): Unit
34
}
35
```
36
37
### Java API Base Class
38
39
```scala { .api }
40
abstract class AbstractShardAllocationStrategy extends ShardAllocationStrategy {
41
// Java-friendly base class for custom implementations
42
}
43
```
44
45
## Built-in Allocation Strategies
46
47
### Least Shard Allocation Strategy (Recommended)
48
49
Modern implementation with absolute and relative limits:
50
51
```scala { .api }
52
object ShardAllocationStrategy {
53
def leastShardAllocationStrategy(
54
absoluteLimit: Int,
55
relativeLimit: Double
56
): ShardAllocationStrategy
57
}
58
59
// Also available via ShardCoordinator
60
object ShardCoordinator {
61
def leastShardAllocationStrategy(
62
absoluteLimit: Int,
63
relativeLimit: Double
64
): ShardAllocationStrategy
65
}
66
```
67
68
**Parameters:**
69
- **`absoluteLimit`**: Maximum number of shards to rebalance per round
70
- **`relativeLimit`**: Fraction of total shards to rebalance per round (< 1.0)
71
72
**Usage Example:**
73
```scala
74
val strategy = ShardCoordinator.leastShardAllocationStrategy(
75
absoluteLimit = 20, // Max 20 shards per rebalance
76
relativeLimit = 0.1 // Max 10% of total shards
77
)
78
79
ClusterSharding(system).start(
80
typeName = "MyEntity",
81
entityProps = Props[MyEntityActor](),
82
settings = ClusterShardingSettings(system),
83
extractEntityId = myExtractEntityId,
84
extractShardId = myExtractShardId,
85
allocationStrategy = strategy,
86
handOffStopMessage = PoisonPill
87
)
88
```
89
90
### Legacy Least Shard Allocation Strategy
91
92
Legacy implementation with threshold-based rebalancing:
93
94
```scala { .api }
95
class LeastShardAllocationStrategy(
96
rebalanceThreshold: Int,
97
maxSimultaneousRebalance: Int
98
) extends ShardAllocationStrategy
99
```
100
101
**Parameters:**
102
- **`rebalanceThreshold`**: Minimum difference in shard count to trigger rebalancing
103
- **`maxSimultaneousRebalance`**: Maximum concurrent rebalancing operations
104
105
**Usage Example:**
106
```scala
107
val strategy = new ShardCoordinator.LeastShardAllocationStrategy(
108
rebalanceThreshold = 10, // Start rebalancing when difference >= 10
109
maxSimultaneousRebalance = 3 // Max 3 concurrent rebalances
110
)
111
```
112
113
### Consistent Hashing Allocation Strategy
114
115
Alternative strategy using consistent hashing:
116
117
```scala { .api }
118
class ConsistentHashingShardAllocationStrategy(
119
virtualNodesFactor: Int,
120
absoluteLimit: Int,
121
relativeLimit: Double
122
) extends ShardAllocationStrategy
123
```
124
125
**Usage Example:**
126
```scala
127
val strategy = new ConsistentHashingShardAllocationStrategy(
128
virtualNodesFactor = 10, // Virtual nodes per physical node
129
absoluteLimit = 20, // Max shards per rebalance round
130
relativeLimit = 0.1 // Max 10% of shards per round
131
)
132
```
133
134
## External Allocation Strategy
135
136
For external control of shard placement:
137
138
```scala { .api }
139
class ExternalShardAllocationStrategy(
140
system: ActorSystem,
141
typeName: String
142
) extends ShardAllocationStrategy
143
```
144
145
This strategy delegates allocation decisions to an external system via the ExternalShardAllocation extension.
146
147
**Usage Example:**
148
```scala
149
import akka.cluster.sharding.external.ExternalShardAllocationStrategy
150
151
val strategy = new ExternalShardAllocationStrategy(system, "MyEntity")
152
153
ClusterSharding(system).start(
154
typeName = "MyEntity",
155
entityProps = Props[MyEntityActor](),
156
settings = ClusterShardingSettings(system),
157
extractEntityId = myExtractEntityId,
158
extractShardId = myExtractShardId,
159
allocationStrategy = strategy,
160
handOffStopMessage = PoisonPill
161
)
162
```
163
164
## Custom Allocation Strategy Implementation
165
166
### Scala Implementation
167
168
```scala
169
class CustomAllocationStrategy extends ShardAllocationStrategy {
170
171
override def allocateShard(
172
requester: ActorRef,
173
shardId: ShardId,
174
currentShardAllocations: Map[ActorRef, IndexedSeq[ShardId]]
175
): Future[ActorRef] = {
176
// Find region with least shards
177
val selectedRegion = currentShardAllocations.minBy(_._2.size)._1
178
Future.successful(selectedRegion)
179
}
180
181
override def rebalance(
182
currentShardAllocations: Map[ActorRef, IndexedSeq[ShardId]],
183
rebalanceInProgress: Set[ShardId]
184
): Future[Set[ShardId]] = {
185
if (rebalanceInProgress.nonEmpty) {
186
// Don't rebalance if already in progress
187
Future.successful(Set.empty)
188
} else {
189
// Find shards to rebalance based on custom logic
190
val (mostLoaded, leastLoaded) = findMostAndLeastLoaded(currentShardAllocations)
191
val shardsToMove = selectShardsToMove(mostLoaded, leastLoaded)
192
Future.successful(shardsToMove.toSet)
193
}
194
}
195
196
private def findMostAndLeastLoaded(
197
allocations: Map[ActorRef, IndexedSeq[ShardId]]
198
): (ActorRef, ActorRef) = {
199
val mostLoaded = allocations.maxBy(_._2.size)._1
200
val leastLoaded = allocations.minBy(_._2.size)._1
201
(mostLoaded, leastLoaded)
202
}
203
204
private def selectShardsToMove(
205
from: ActorRef,
206
to: ActorRef
207
): List[ShardId] = {
208
// Custom logic to select which shards to move
209
List.empty // Placeholder
210
}
211
}
212
```
213
214
### Java Implementation
215
216
```java
217
public class JavaCustomAllocationStrategy extends AbstractShardAllocationStrategy {
218
219
@Override
220
public CompletionStage<ActorRef> allocateShard(
221
ActorRef requester,
222
String shardId,
223
Map<ActorRef, ImmutableList<String>> currentShardAllocations) {
224
225
// Find region with minimum shard count
226
ActorRef selectedRegion = currentShardAllocations.entrySet().stream()
227
.min(Map.Entry.comparingByValue((a, b) -> Integer.compare(a.size(), b.size())))
228
.map(Map.Entry::getKey)
229
.orElse(requester);
230
231
return CompletableFuture.completedFuture(selectedRegion);
232
}
233
234
@Override
235
public CompletionStage<Set<String>> rebalance(
236
Map<ActorRef, ImmutableList<String>> currentShardAllocations,
237
Set<String> rebalanceInProgress) {
238
239
if (!rebalanceInProgress.isEmpty()) {
240
return CompletableFuture.completedFuture(Collections.emptySet());
241
}
242
243
// Custom rebalancing logic
244
return CompletableFuture.completedFuture(Collections.emptySet());
245
}
246
}
247
```
248
249
## Strategy Selection Guidelines
250
251
### Use Least Shard Allocation Strategy When:
252
- You want even distribution of shards across nodes
253
- New nodes should gradually take load from existing nodes
254
- You need predictable rebalancing behavior
255
- You don't have specific placement requirements
256
257
### Use Consistent Hashing Strategy When:
258
- You want to minimize shard movement when nodes join/leave
259
- You have stable cluster topology
260
- You prefer locality over perfect balance
261
- Network partitions are a concern
262
263
### Use External Allocation Strategy When:
264
- You need centralized control over shard placement
265
- You have complex business logic for placement decisions
266
- You're integrating with external orchestration systems
267
- You need real-time control over shard locations
268
269
### Use Custom Strategy When:
270
- Built-in strategies don't meet your requirements
271
- You have domain-specific placement logic
272
- You need to integrate with custom monitoring/metrics
273
- You have unique performance characteristics
274
275
## Configuration Integration
276
277
Strategies can be configured in `application.conf`:
278
279
```hocon
280
akka.cluster.sharding {
281
least-shard-allocation-strategy {
282
# New algorithm parameters
283
rebalance-absolute-limit = 20
284
rebalance-relative-limit = 0.1
285
286
# Legacy algorithm parameters (deprecated)
287
rebalance-threshold = 10
288
max-simultaneous-rebalance = 3
289
}
290
}
291
```
292
293
## Performance Considerations
294
295
### Allocation Performance
296
- **`allocateShard`** should return quickly (< 100ms)
297
- Avoid blocking operations in allocation logic
298
- Cache expensive computations if possible
299
300
### Rebalancing Performance
301
- **`rebalance`** is called periodically (default: 10s intervals)
302
- Don't return too many shards for simultaneous rebalancing
303
- Consider cluster load when making rebalancing decisions
304
305
### Resource Usage
306
- Strategies should be stateless or use minimal state
307
- Avoid excessive memory allocation in hot paths
308
- Be careful with futures and thread pool usage