or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

allocation-strategies.mdconfiguration.mdcore-extension.mdexternal-allocation.mdindex.mdmessage-routing.mdmonitoring.md

allocation-strategies.mddocs/

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