or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cluster-management.mdcluster-routing.mdconfiguration-and-management.mdevents-and-state.mdextensibility.mdindex.mdmembers-and-status.md

members-and-status.mddocs/

0

# Members and Status

1

2

Member representation, status management, and member lifecycle including addressing and role-based organization. This covers how cluster nodes are represented, their lifecycle states, and how to work with member information.

3

4

## Capabilities

5

6

### Member Representation

7

8

The core representation of a cluster member containing address, status, and role information.

9

10

```scala { .api }

11

/**

12

* Represents the address, current status, and roles of a cluster member node.

13

* Note: hashCode and equals are solely based on the underlying Address, not its MemberStatus and roles.

14

*/

15

class Member private[cluster] (

16

val uniqueAddress: UniqueAddress,

17

private[cluster] val upNumber: Int,

18

val status: MemberStatus,

19

val roles: Set[String]

20

) extends Serializable {

21

/** The network address of this member */

22

def address: Address

23

24

/** Data center this member belongs to */

25

lazy val dataCenter: DataCenter

26

27

/** Check if member has specific role */

28

def hasRole(role: String): Boolean

29

30

/** Java API: get all roles as Java Set */

31

def getRoles: java.util.Set[String]

32

33

/**

34

* Is this member older, has been part of cluster longer, than another member.

35

* Only correct when comparing two existing members in a cluster.

36

* Throws IllegalArgumentException if members from different data centers.

37

*/

38

def isOlderThan(other: Member): Boolean

39

40

/** Create copy with new status */

41

def copy(status: MemberStatus): Member

42

43

/** Create copy with Up status and new up number */

44

def copyUp(upNumber: Int): Member

45

}

46

```

47

48

**Usage Examples:**

49

50

```scala

51

// Working with member information

52

cluster.state.members.foreach { member =>

53

println(s"Member: ${member.address}")

54

println(s"Status: ${member.status}")

55

println(s"Roles: ${member.roles.mkString(", ")}")

56

println(s"Data Center: ${member.dataCenter}")

57

58

// Check for specific roles

59

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

60

println("This is a frontend node")

61

}

62

63

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

64

println("This is a backend node")

65

}

66

}

67

68

// Compare member ages (within same data center)

69

val members = cluster.state.members.toList

70

if (members.size >= 2) {

71

val member1 = members(0)

72

val member2 = members(1)

73

74

if (member1.dataCenter == member2.dataCenter) {

75

if (member1.isOlderThan(member2)) {

76

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

77

}

78

}

79

}

80

```

81

82

### Member Status

83

84

Enumeration of all possible member states in the cluster lifecycle.

85

86

```scala { .api }

87

/**

88

* Defines the current status of a cluster member node.

89

* Can be one of: Joining, WeaklyUp, Up, Leaving, Exiting, Down, and Removed.

90

*/

91

sealed abstract class MemberStatus

92

93

object MemberStatus {

94

/** Member is in the process of joining the cluster */

95

case object Joining extends MemberStatus

96

97

/**

98

* Member is weakly up - joined but not all nodes have seen it yet.

99

* This happens when convergence cannot be reached due to unreachable nodes.

100

*/

101

case object WeaklyUp extends MemberStatus

102

103

/** Member is fully up and participating in the cluster */

104

case object Up extends MemberStatus

105

106

/** Member is gracefully leaving the cluster */

107

case object Leaving extends MemberStatus

108

109

/** Member is in the exiting phase of leaving */

110

case object Exiting extends MemberStatus

111

112

/** Member has been marked as down (forceful removal) */

113

case object Down extends MemberStatus

114

115

/** Member has been completely removed from the cluster */

116

case object Removed extends MemberStatus

117

118

// Java API accessors

119

def joining: MemberStatus = Joining

120

def weaklyUp: MemberStatus = WeaklyUp

121

def up: MemberStatus = Up

122

def leaving: MemberStatus = Leaving

123

def exiting: MemberStatus = Exiting

124

def down: MemberStatus = Down

125

def removed: MemberStatus = Removed

126

}

127

```

128

129

**Usage Examples:**

130

131

```scala

132

import akka.cluster.MemberStatus._

133

134

// Check member status

135

cluster.state.members.foreach { member =>

136

member.status match {

137

case Joining => println(s"${member.address} is joining")

138

case WeaklyUp => println(s"${member.address} is weakly up")

139

case Up => println(s"${member.address} is up and ready")

140

case Leaving => println(s"${member.address} is leaving")

141

case Exiting => println(s"${member.address} is exiting")

142

case Down => println(s"${member.address} is down")

143

case Removed => println(s"${member.address} was removed")

144

}

145

}

146

147

// Filter members by status

148

val upMembers = cluster.state.members.filter(_.status == Up)

149

val joiningMembers = cluster.state.members.filter(_.status == Joining)

150

151

println(s"Up members: ${upMembers.size}")

152

println(s"Joining members: ${joiningMembers.size}")

153

```

154

155

### Unique Addressing

156

157

Unique addressing system that distinguishes different incarnations of nodes with the same network address.

158

159

```scala { .api }

160

/**

161

* Member identifier consisting of address and random uid.

162

* The uid is needed to be able to distinguish different

163

* incarnations of a member with same hostname and port.

164

*/

165

case class UniqueAddress(address: Address, longUid: Long) extends Ordered[UniqueAddress] {

166

/** Compare unique addresses for ordering */

167

def compare(that: UniqueAddress): Int

168

}

169

170

object UniqueAddress {

171

/** Create unique address with address and uid */

172

def apply(address: Address, uid: Long): UniqueAddress

173

}

174

```

175

176

**Usage Examples:**

177

178

```scala

179

// Access unique address information

180

val member = cluster.selfMember

181

println(s"Address: ${member.uniqueAddress.address}")

182

println(s"Unique ID: ${member.uniqueAddress.longUid}")

183

184

// Unique addresses allow distinguishing restarts

185

val addr1 = UniqueAddress(Address("akka.tcp", "System", "host", 2551), 123456L)

186

val addr2 = UniqueAddress(Address("akka.tcp", "System", "host", 2551), 789012L)

187

// Same network address but different UIDs - different incarnations

188

```

189

190

### Member Factory and Utilities

191

192

Factory methods and utilities for working with members and ordering.

193

194

```scala { .api }

195

object Member {

196

/** Empty member set constant */

197

val none: Set[Member] = Set.empty[Member]

198

199

/**

200

* Address ordering type class, sorts addresses by host and port

201

*/

202

implicit val addressOrdering: Ordering[Address]

203

204

/**

205

* Member ordering type class, sorts members by host and port

206

*/

207

implicit val ordering: Ordering[Member]

208

209

/**

210

* Sort members by age, i.e. using Member#isOlderThan.

211

* Only makes sense to compare members of same data center.

212

*/

213

val ageOrdering: Ordering[Member]

214

215

/**

216

* Picks the Member with the highest "priority" MemberStatus.

217

*/

218

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

219

}

220

```

221

222

**Usage Examples:**

223

224

```scala

225

// Sort members by address

226

val sortedByAddress = cluster.state.members.toList.sorted(Member.ordering)

227

228

// Sort members by age (oldest first)

229

val membersList = cluster.state.members.toList

230

val sortedByAge = membersList.sorted(Member.ageOrdering)

231

232

// Find member with highest priority status

233

val members = List(member1, member2)

234

val highestPriority = members.reduceLeft(Member.highestPriorityOf)

235

236

// Work with empty member set

237

if (cluster.state.members == Member.none) {

238

println("No members in cluster")

239

}

240

```

241

242

### Role-Based Organization

243

244

Working with member roles for organizing cluster nodes by function.

245

246

```scala { .api }

247

// Member role methods

248

def hasRole(role: String): Boolean

249

def roles: Set[String]

250

def getRoles: java.util.Set[String] // Java API

251

252

// Cluster-level role information

253

def selfRoles: Set[String] // From Cluster class

254

def getSelfRoles: java.util.Set[String] // Java API

255

```

256

257

**Usage Examples:**

258

259

```scala

260

// Configure roles in application.conf

261

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

262

263

// Check self roles

264

println(s"My roles: ${cluster.selfRoles}")

265

266

// Find members by role

267

val frontendMembers = cluster.state.members.filter(_.hasRole("frontend"))

268

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

269

val dbMembers = cluster.state.members.filter(_.hasRole("database"))

270

271

println(s"Frontend nodes: ${frontendMembers.size}")

272

println(s"Backend nodes: ${backendMembers.size}")

273

println(s"Database nodes: ${dbMembers.size}")

274

275

// Route work based on roles

276

def routeToBackend(message: Any): Unit = {

277

val backendAddresses = cluster.state.members

278

.filter(_.hasRole("backend"))

279

.filter(_.status == Up)

280

.map(_.address)

281

282

if (backendAddresses.nonEmpty) {

283

// Send to backend nodes

284

backendAddresses.foreach { addr =>

285

// Route message to backend at addr

286

}

287

}

288

}

289

```

290

291

### Member Status Transitions

292

293

Understanding valid member status transitions and lifecycle.

294

295

```scala { .api }

296

// Status transition validation is handled internally

297

// Valid transitions:

298

// Joining -> WeaklyUp, Up, Leaving, Down, Removed

299

// WeaklyUp -> Up, Leaving, Down, Removed

300

// Up -> Leaving, Down, Removed

301

// Leaving -> Exiting, Down, Removed

302

// Down -> Removed

303

// Exiting -> Removed, Down

304

// Removed -> (no transitions - terminal state)

305

```

306

307

**Usage Examples:**

308

309

```scala

310

// Monitor member lifecycle in event handler

311

class MemberLifecycleMonitor extends Actor with ActorLogging {

312

var memberHistory = Map.empty[Address, List[MemberStatus]]

313

314

def receive = {

315

case event: MemberEvent =>

316

val member = event.member

317

val history = memberHistory.getOrElse(member.address, List.empty)

318

val newHistory = member.status :: history

319

memberHistory = memberHistory + (member.address -> newHistory)

320

321

log.info("Member {} status: {} (history: {})",

322

member.address, member.status, newHistory.reverse.mkString(" -> "))

323

324

// Detect problematic transitions

325

if (member.status == Down && history.headOption.contains(Up)) {

326

log.warning("Member {} went directly from Up to Down - possible network failure",

327

member.address)

328

}

329

}

330

}

331

```

332

333

### Data Center Support

334

335

Multi-data center clustering support for geographic distribution.

336

337

```scala { .api }

338

// Data center information

339

def dataCenter: DataCenter // From Member

340

def selfDataCenter: DataCenter // From Cluster

341

type DataCenter = String

342

343

// Data center discovery from current state

344

def allDataCenters: Set[String] // From CurrentClusterState

345

def getAllDataCenters: java.util.Set[String] // Java API

346

```

347

348

**Usage Examples:**

349

350

```scala

351

// Work with data centers

352

val state = cluster.state

353

println(s"All data centers: ${state.allDataCenters}")

354

println(s"My data center: ${cluster.selfDataCenter}")

355

356

// Organize members by data center

357

val membersByDc = cluster.state.members.groupBy(_.dataCenter)

358

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

359

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

360

members.foreach(m => println(s" ${m.address} (${m.status})"))

361

}

362

363

// Find members in same data center

364

val sameDcMembers = cluster.state.members.filter(_.dataCenter == cluster.selfDataCenter)

365

val otherDcMembers = cluster.state.members.filter(_.dataCenter != cluster.selfDataCenter)

366

367

// Age comparison only valid within same data center

368

val myDcMembers = cluster.state.members.filter(_.dataCenter == cluster.selfDataCenter).toList

369

if (myDcMembers.size >= 2) {

370

try {

371

val oldest = myDcMembers.minBy(_.upNumber)

372

println(s"Oldest member in my DC: ${oldest.address}")

373

} catch {

374

case _: IllegalArgumentException =>

375

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

376

}

377

}

378

```

379

380

## Types

381

382

```scala { .api }

383

// Core member types

384

class Member(uniqueAddress: UniqueAddress, status: MemberStatus, roles: Set[String])

385

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

386

type DataCenter = String

387

388

// Member status enumeration

389

sealed abstract class MemberStatus

390

case object Joining extends MemberStatus

391

case object WeaklyUp extends MemberStatus

392

case object Up extends MemberStatus

393

case object Leaving extends MemberStatus

394

case object Exiting extends MemberStatus

395

case object Down extends MemberStatus

396

case object Removed extends MemberStatus

397

398

// Ordering types

399

implicit val addressOrdering: Ordering[Address]

400

implicit val ordering: Ordering[Member]

401

val ageOrdering: Ordering[Member]

402

```