or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cluster-management.mdcluster-routing.mdconfiguration.mdevent-system.mdindex.mdmember-management.mdsplit-brain-resolution.md

member-management.mddocs/

0

# Member Management

1

2

Member management in Akka Cluster centers around the `Member` class which represents cluster nodes and their current state. Members progress through well-defined lifecycle stages and can be organized by roles and data centers.

3

4

## Member Representation

5

6

### Member Class

7

8

```scala { .api }

9

class Member private[cluster] (

10

val uniqueAddress: UniqueAddress,

11

private[cluster] val upNumber: Int,

12

val status: MemberStatus,

13

val roles: Set[String],

14

val appVersion: Version

15

) extends Serializable {

16

def address: Address

17

def dataCenter: DataCenter

18

def hasRole(role: String): Boolean

19

def getRoles: java.util.Set[String]

20

def isOlderThan(other: Member): Boolean

21

def copy(status: MemberStatus): Member

22

def copyUp(upNumber: Int): Member

23

24

// Standard methods

25

override def hashCode: Int

26

override def equals(other: Any): Boolean

27

override def toString: String

28

}

29

```

30

31

### Unique Address

32

33

```scala { .api }

34

class UniqueAddress(val address: Address, val longUid: Long)

35

extends Product with Serializable with Ordered[UniqueAddress] {

36

def address: Address

37

def longUid: Long

38

39

@deprecated("Use longUid instead", since = "2.4.11")

40

def uid: Int = longUid.toInt

41

42

def compare(that: UniqueAddress): Int

43

override def productArity: Int = 2

44

override def productElement(n: Int): Any

45

override def canEqual(that: Any): Boolean

46

override def hashCode: Int

47

override def equals(other: Any): Boolean

48

override def toString: String

49

}

50

```

51

52

### Member Properties

53

54

```scala

55

val member: Member = cluster.selfMember

56

57

// Core identification

58

val address: Address = member.address // Network address

59

val uniqueAddress: UniqueAddress = member.uniqueAddress // Address + unique ID

60

val uid: Long = member.uniqueAddress.uid // Unique identifier

61

62

// Member metadata

63

val status: MemberStatus = member.status // Current lifecycle status

64

val roles: Set[String] = member.roles // Assigned roles

65

val dataCenter: String = member.dataCenter // Data center assignment

66

val appVersion: Version = member.appVersion // Application version

67

68

println(s"Member: ${address}, Status: ${status}, DC: ${dataCenter}")

69

```

70

71

## Member Status Lifecycle

72

73

### Status Enumeration

74

75

```scala { .api }

76

sealed abstract class MemberStatus

77

78

case object Joining extends MemberStatus

79

case object WeaklyUp extends MemberStatus

80

case object Up extends MemberStatus

81

case object Leaving extends MemberStatus

82

case object Exiting extends MemberStatus

83

case object Down extends MemberStatus

84

case object Removed extends MemberStatus

85

case object PreparingForShutdown extends MemberStatus

86

case object ReadyForShutdown extends MemberStatus

87

88

object MemberStatus {

89

// Java API accessors

90

def joining(): MemberStatus = Joining

91

def weaklyUp(): MemberStatus = WeaklyUp

92

def up(): MemberStatus = Up

93

def leaving(): MemberStatus = Leaving

94

def exiting(): MemberStatus = Exiting

95

def down(): MemberStatus = Down

96

def removed(): MemberStatus = Removed

97

def shuttingDown(): MemberStatus = PreparingForShutdown // Java name mapping

98

def shutDown(): MemberStatus = ReadyForShutdown // Java name mapping

99

}

100

```

101

102

### Status Transitions

103

104

Normal lifecycle progression:

105

```

106

Joining → WeaklyUp → Up → Leaving → Exiting → Removed

107

```

108

109

Failure scenarios:

110

```

111

Any Status → Down → Removed

112

```

113

114

Coordinated shutdown:

115

```

116

Up → PreparingForShutdown → ReadyForShutdown → Exiting → Removed

117

```

118

119

Complete allowed transitions:

120

```

121

From PreparingForShutdown: ReadyForShutdown, Removed, Leaving, Down

122

From ReadyForShutdown: Removed, Leaving, Down

123

```

124

125

### Status Descriptions

126

127

- **Joining**: Node is attempting to join cluster

128

- **WeaklyUp**: Node is up but cluster hasn't reached minimum size

129

- **Up**: Node is fully operational cluster member

130

- **Leaving**: Node is gracefully leaving cluster

131

- **Exiting**: Node is in exit process, will be removed

132

- **Down**: Node marked as failed/unreachable

133

- **Removed**: Node completely removed from cluster

134

- **PreparingForShutdown**: Node preparing for coordinated shutdown

135

- **ReadyForShutdown**: Node ready for coordinated shutdown

136

137

## Role-Based Operations

138

139

### Role Assignment

140

141

Roles are configured during system startup and cannot be changed at runtime:

142

143

```hocon

144

akka.cluster.roles = ["frontend", "backend", "database"]

145

```

146

147

### Role Checking

148

149

```scala

150

val member: Member = cluster.selfMember

151

152

// Check for specific role

153

if (member.hasRole("backend")) {

154

// Backend-specific logic

155

startBackendServices()

156

}

157

158

// Check multiple roles

159

val isFrontend = member.hasRole("frontend")

160

val isDatabase = member.hasRole("database")

161

162

// Get all roles

163

val allRoles: Set[String] = member.roles

164

println(s"Node roles: ${allRoles.mkString(", ")}")

165

166

// Java API

167

val rolesJava: java.util.Set[String] = member.getRoles

168

```

169

170

### Role-Based Member Filtering

171

172

```scala

173

val currentState = cluster.state

174

175

// Find all backend members

176

val backendMembers = currentState.members.filter(_.hasRole("backend"))

177

178

// Find available (Up or WeaklyUp) frontend members

179

val availableFrontends = currentState.members

180

.filter(m => m.hasRole("frontend") &&

181

(m.status == MemberStatus.Up || m.status == MemberStatus.WeaklyUp))

182

183

// Count members by role

184

val membersByRole = currentState.members.groupBy(_.roles).view.mapValues(_.size)

185

println(s"Members by role: $membersByRole")

186

```

187

188

## Member Comparison and Ordering

189

190

### Age Comparison

191

192

```scala { .api }

193

// Compare member age (same data center only)

194

def isOlderThan(other: Member): Boolean

195

```

196

197

Usage:

198

```scala

199

val member1: Member = // ...

200

val member2: Member = // ...

201

202

try {

203

if (member1.isOlderThan(member2)) {

204

println(s"${member1.address} is older than ${member2.address}")

205

}

206

} catch {

207

case _: IllegalArgumentException =>

208

println("Cannot compare members from different data centers")

209

}

210

```

211

212

### Member Ordering

213

214

```scala { .api }

215

object Member {

216

val ordering: Ordering[Member]

217

val ageOrdering: Ordering[Member]

218

val addressOrdering: Ordering[Address]

219

val none: Set[Member]

220

221

// Utility methods

222

def highestPriorityOf(m1: Member, m2: Member): Member

223

}

224

```

225

226

Usage:

227

```scala

228

val members: Set[Member] = cluster.state.members.toSet

229

230

// Default ordering (by address)

231

val sortedMembers = members.toSeq.sorted(Member.ordering)

232

233

// Age-based ordering (oldest first)

234

val membersByAge = members.toSeq.sorted(Member.ageOrdering)

235

236

// Address-based ordering

237

val addresses = members.map(_.address).toSeq.sorted(Member.addressOrdering)

238

```

239

240

## Data Center Support

241

242

### Data Center Assignment

243

244

Data centers are assigned via special roles with the `dc-` prefix:

245

246

```hocon

247

akka.cluster {

248

roles = ["dc-east", "backend"]

249

multi-data-center.self-data-center = "east"

250

}

251

```

252

253

### Data Center Operations

254

255

```scala

256

val member: Member = cluster.selfMember

257

258

// Get data center

259

val dataCenter: String = member.dataCenter

260

println(s"Member data center: $dataCenter")

261

262

// Filter by data center

263

val currentState = cluster.state

264

val localMembers = currentState.members.filter(_.dataCenter == cluster.selfDataCenter)

265

val remoteMembers = currentState.members.filter(_.dataCenter != cluster.selfDataCenter)

266

267

// Group by data center

268

val membersByDC = currentState.members.groupBy(_.dataCenter)

269

membersByDC.foreach { case (dc, members) =>

270

println(s"Data center '$dc': ${members.size} members")

271

}

272

```

273

274

## Member State Changes

275

276

### Creating Member Copies

277

278

```scala { .api }

279

def copy(status: MemberStatus): Member

280

def copyUp(upNumber: Int): Member

281

```

282

283

Usage (typically internal to Akka):

284

```scala

285

val member: Member = // existing member

286

val updatedMember = member.copy(MemberStatus.Up)

287

val upMember = member.copyUp(upNumber = 42)

288

```

289

290

### Allowed Status Transitions

291

292

Only specific status transitions are allowed:

293

- From `Joining` to `WeaklyUp` or `Up`

294

- From `WeaklyUp` to `Up`

295

- From `Up` to `Leaving` or `Down` or `PreparingForShutdown`

296

- From `Leaving` to `Exiting` or `Down`

297

- From `Exiting` to `Removed`

298

- From `Down` to `Removed`

299

- From `PreparingForShutdown` to `ReadyForShutdown` or `Down`

300

- From `ReadyForShutdown` to `Exiting` or `Down`

301

302

## Application Version Management

303

304

### Version Compatibility

305

306

Members carry application version information for compatibility checking:

307

308

```scala

309

val member: Member = cluster.selfMember

310

val version: Version = member.appVersion

311

312

println(s"App version: ${version.version}")

313

314

// Version comparison (if needed)

315

val otherVersion = Version("1.2.0")

316

if (version >= otherVersion) {

317

// Compatible version

318

}

319

```

320

321

### Dynamic Version Setting

322

323

```scala

324

import scala.concurrent.Future

325

import akka.util.Version

326

327

// Set version before joining

328

val versionFuture: Future[Version] = loadVersionFromExternalSystem()

329

cluster.setAppVersionLater(versionFuture)

330

cluster.joinSeedNodes(seedNodes)

331

```

332

333

## Member Information Access

334

335

### Self Member Access

336

337

```scala

338

val cluster = Cluster(system)

339

340

// Current node as member

341

val selfMember: Member = cluster.selfMember

342

val selfAddress: Address = cluster.selfAddress

343

val selfUniqueAddress: UniqueAddress = cluster.selfUniqueAddress

344

val selfRoles: Set[String] = cluster.selfRoles

345

val selfDataCenter: String = cluster.selfDataCenter

346

347

// Java API for roles

348

val selfRolesJava: java.util.Set[String] = cluster.getSelfRoles

349

```

350

351

### Cluster Member Access

352

353

```scala

354

val state: CurrentClusterState = cluster.state

355

356

// All members

357

val allMembers: immutable.SortedSet[Member] = state.members

358

359

// Filter by status

360

val upMembers = allMembers.filter(_.status == MemberStatus.Up)

361

val joiningMembers = allMembers.filter(_.status == MemberStatus.Joining)

362

363

// Find specific member

364

val memberOpt = allMembers.find(_.address == targetAddress)

365

366

// Unreachable members

367

val unreachableMembers: Set[Member] = state.unreachable

368

```

369

370

## Member Lifecycle Monitoring

371

372

### Complete Member Tracking Example

373

374

```scala

375

import akka.actor.{Actor, ActorLogging}

376

import akka.cluster.{Cluster, Member, MemberStatus}

377

import akka.cluster.ClusterEvent._

378

import scala.collection.mutable

379

380

class MemberTracker extends Actor with ActorLogging {

381

val cluster = Cluster(context.system)

382

val members = mutable.Map[Address, Member]()

383

384

override def preStart(): Unit = {

385

cluster.subscribe(self, classOf[MemberEvent])

386

}

387

388

override def postStop(): Unit = {

389

cluster.unsubscribe(self)

390

}

391

392

def receive = {

393

case state: CurrentClusterState =>

394

members.clear()

395

state.members.foreach { member =>

396

members(member.address) = member

397

logMemberInfo(member, "Initial")

398

}

399

400

case MemberJoined(member) =>

401

members(member.address) = member

402

logMemberInfo(member, "Joined")

403

404

case MemberUp(member) =>

405

members(member.address) = member

406

logMemberInfo(member, "Up")

407

408

case MemberLeft(member) =>

409

members(member.address) = member

410

logMemberInfo(member, "Left")

411

412

case MemberRemoved(member, previousStatus) =>

413

members.remove(member.address)

414

log.info("Member removed: {} (was: {})", member.address, previousStatus)

415

416

case event: MemberEvent =>

417

members(event.member.address) = event.member

418

logMemberInfo(event.member, event.getClass.getSimpleName)

419

}

420

421

def logMemberInfo(member: Member, event: String): Unit = {

422

log.info("{}: {} - Status: {}, Roles: {}, DC: {}, Version: {}",

423

event, member.address, member.status,

424

member.roles.mkString(","), member.dataCenter, member.appVersion)

425

}

426

}

427

```