2012-07-05 13:55:08 +02:00
|
|
|
|
/**
|
2013-01-09 01:47:48 +01:00
|
|
|
|
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
2012-07-05 13:55:08 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
package akka.cluster
|
|
|
|
|
|
|
2012-07-23 13:53:20 +02:00
|
|
|
|
import language.implicitConversions
|
|
|
|
|
|
|
2012-11-15 12:33:11 +01:00
|
|
|
|
import scala.collection.immutable
|
2012-07-05 13:55:08 +02:00
|
|
|
|
import scala.collection.GenTraversableOnce
|
|
|
|
|
|
import akka.actor.Address
|
|
|
|
|
|
import MemberStatus._
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-14 20:32:43 +01:00
|
|
|
|
* Represents the address, current status, and roles of a cluster member node.
|
2012-07-05 13:55:08 +02:00
|
|
|
|
*
|
2013-03-14 20:32:43 +01:00
|
|
|
|
* Note: `hashCode` and `equals` are solely based on the underlying `Address`, not its `MemberStatus`
|
|
|
|
|
|
* and roles.
|
2012-07-05 13:55:08 +02:00
|
|
|
|
*/
|
2013-04-04 17:56:29 +02:00
|
|
|
|
@SerialVersionUID(1L)
|
|
|
|
|
|
class Member(val address: Address, val status: MemberStatus, val roles: Set[String]) extends Serializable {
|
2012-07-05 13:55:08 +02:00
|
|
|
|
override def hashCode = address.##
|
2013-03-14 20:32:43 +01:00
|
|
|
|
override def equals(other: Any) = other match {
|
|
|
|
|
|
case m: Member ⇒ address == m.address
|
|
|
|
|
|
case _ ⇒ false
|
|
|
|
|
|
}
|
2012-07-05 13:55:08 +02:00
|
|
|
|
override def toString = "Member(address = %s, status = %s)" format (address, status)
|
2013-03-14 20:32:43 +01:00
|
|
|
|
|
|
|
|
|
|
def hasRole(role: String): Boolean = roles.contains(role)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Java API
|
|
|
|
|
|
*/
|
|
|
|
|
|
def getRoles: java.util.Set[String] =
|
|
|
|
|
|
scala.collection.JavaConverters.setAsJavaSetConverter(roles).asJava
|
2013-04-05 12:38:09 +02:00
|
|
|
|
|
|
|
|
|
|
def copy(status: MemberStatus): Member = {
|
|
|
|
|
|
val oldStatus = this.status
|
|
|
|
|
|
if (status == oldStatus) this
|
|
|
|
|
|
else {
|
|
|
|
|
|
require(allowedTransitions(oldStatus)(status),
|
|
|
|
|
|
s"Invalid member status transition [ ${this} -> ${status}]")
|
|
|
|
|
|
new Member(address, status, roles)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-07-05 13:55:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Module with factory and ordering methods for Member instances.
|
|
|
|
|
|
*/
|
|
|
|
|
|
object Member {
|
|
|
|
|
|
|
2012-07-07 20:55:02 +02:00
|
|
|
|
val none = Set.empty[Member]
|
|
|
|
|
|
|
2013-04-05 12:38:09 +02:00
|
|
|
|
def apply(address: Address, status: MemberStatus, roles: Set[String]): Member =
|
|
|
|
|
|
new Member(address, status, roles)
|
|
|
|
|
|
|
2012-07-05 13:55:08 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* `Address` ordering type class, sorts addresses by host and port.
|
|
|
|
|
|
*/
|
|
|
|
|
|
implicit val addressOrdering: Ordering[Address] = Ordering.fromLessThan[Address] { (a, b) ⇒
|
2013-01-15 09:35:07 +01:00
|
|
|
|
// cluster node identifier is the host and port of the address; protocol and system is assumed to be the same
|
2012-07-05 13:55:08 +02:00
|
|
|
|
if (a.host != b.host) a.host.getOrElse("").compareTo(b.host.getOrElse("")) < 0
|
|
|
|
|
|
else if (a.port != b.port) a.port.getOrElse(0) < b.port.getOrElse(0)
|
|
|
|
|
|
else false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-03-06 16:39:22 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
|
* Orders the members by their address except that members with status
|
|
|
|
|
|
* Joining, Exiting and Down are ordered last (in that order).
|
|
|
|
|
|
*/
|
|
|
|
|
|
private[cluster] val leaderStatusOrdering: Ordering[Member] = Ordering.fromLessThan[Member] { (a, b) ⇒
|
|
|
|
|
|
(a.status, b.status) match {
|
|
|
|
|
|
case (as, bs) if as == bs ⇒ ordering.compare(a, b) <= 0
|
|
|
|
|
|
case (Down, _) ⇒ false
|
|
|
|
|
|
case (_, Down) ⇒ true
|
|
|
|
|
|
case (Exiting, _) ⇒ false
|
|
|
|
|
|
case (_, Exiting) ⇒ true
|
|
|
|
|
|
case (Joining, _) ⇒ false
|
|
|
|
|
|
case (_, Joining) ⇒ true
|
|
|
|
|
|
case _ ⇒ ordering.compare(a, b) <= 0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-05 13:55:08 +02:00
|
|
|
|
/**
|
2012-08-19 21:48:39 +02:00
|
|
|
|
* `Member` ordering type class, sorts members by host and port.
|
2012-07-05 13:55:08 +02:00
|
|
|
|
*/
|
2012-08-19 21:48:39 +02:00
|
|
|
|
implicit val ordering: Ordering[Member] = new Ordering[Member] {
|
|
|
|
|
|
def compare(a: Member, b: Member): Int = addressOrdering.compare(a.address, b.address)
|
2012-07-05 13:55:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def pickHighestPriority(a: Set[Member], b: Set[Member]): Set[Member] = {
|
|
|
|
|
|
// group all members by Address => Seq[Member]
|
|
|
|
|
|
val groupedByAddress = (a.toSeq ++ b.toSeq).groupBy(_.address)
|
|
|
|
|
|
// pick highest MemberStatus
|
2012-07-07 20:55:02 +02:00
|
|
|
|
(Member.none /: groupedByAddress) {
|
2012-07-05 13:55:08 +02:00
|
|
|
|
case (acc, (_, members)) ⇒ acc + members.reduceLeft(highestPriorityOf)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Picks the Member with the highest "priority" MemberStatus.
|
|
|
|
|
|
*/
|
|
|
|
|
|
def highestPriorityOf(m1: Member, m2: Member): Member = (m1.status, m2.status) match {
|
|
|
|
|
|
case (Removed, _) ⇒ m1
|
|
|
|
|
|
case (_, Removed) ⇒ m2
|
|
|
|
|
|
case (Down, _) ⇒ m1
|
|
|
|
|
|
case (_, Down) ⇒ m2
|
|
|
|
|
|
case (Exiting, _) ⇒ m1
|
|
|
|
|
|
case (_, Exiting) ⇒ m2
|
|
|
|
|
|
case (Leaving, _) ⇒ m1
|
|
|
|
|
|
case (_, Leaving) ⇒ m2
|
|
|
|
|
|
case (Up, Joining) ⇒ m2
|
|
|
|
|
|
case (Joining, Up) ⇒ m1
|
|
|
|
|
|
case (Joining, Joining) ⇒ m1
|
|
|
|
|
|
case (Up, Up) ⇒ m1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Defines the current status of a cluster member node
|
|
|
|
|
|
*
|
|
|
|
|
|
* Can be one of: Joining, Up, Leaving, Exiting and Down.
|
|
|
|
|
|
*/
|
2013-04-04 17:56:29 +02:00
|
|
|
|
abstract class MemberStatus
|
2012-07-05 13:55:08 +02:00
|
|
|
|
|
|
|
|
|
|
object MemberStatus {
|
2013-04-04 17:56:29 +02:00
|
|
|
|
@SerialVersionUID(1L) case object Joining extends MemberStatus
|
|
|
|
|
|
@SerialVersionUID(1L) case object Up extends MemberStatus
|
|
|
|
|
|
@SerialVersionUID(1L) case object Leaving extends MemberStatus
|
|
|
|
|
|
@SerialVersionUID(1L) case object Exiting extends MemberStatus
|
|
|
|
|
|
@SerialVersionUID(1L) case object Down extends MemberStatus
|
|
|
|
|
|
@SerialVersionUID(1L) case object Removed extends MemberStatus
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
|
* Java API: retrieve the “joining” status singleton
|
2012-10-04 10:55:22 +02:00
|
|
|
|
*/
|
2012-10-05 08:08:25 +02:00
|
|
|
|
def joining: MemberStatus = Joining
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
|
* Java API: retrieve the “up” status singleton
|
2012-10-04 10:55:22 +02:00
|
|
|
|
*/
|
2012-10-05 08:08:25 +02:00
|
|
|
|
def up: MemberStatus = Up
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
|
* Java API: retrieve the “leaving” status singleton
|
2012-10-04 10:55:22 +02:00
|
|
|
|
*/
|
2012-10-05 08:08:25 +02:00
|
|
|
|
def leaving: MemberStatus = Leaving
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
|
* Java API: retrieve the “exiting” status singleton
|
2012-10-04 10:55:22 +02:00
|
|
|
|
*/
|
2012-10-05 08:08:25 +02:00
|
|
|
|
def exiting: MemberStatus = Exiting
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
|
* Java API: retrieve the “down” status singleton
|
2012-10-04 10:55:22 +02:00
|
|
|
|
*/
|
2012-10-05 08:08:25 +02:00
|
|
|
|
def down: MemberStatus = Down
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
|
* Java API: retrieve the “removed” status singleton
|
2012-10-04 10:55:22 +02:00
|
|
|
|
*/
|
2012-10-05 08:08:25 +02:00
|
|
|
|
def removed: MemberStatus = Removed
|
2013-04-05 12:38:09 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
|
*/
|
|
|
|
|
|
private[cluster] val allowedTransitions: Map[MemberStatus, Set[MemberStatus]] =
|
|
|
|
|
|
Map(
|
|
|
|
|
|
Joining -> Set(Up, Down, Removed),
|
|
|
|
|
|
Up -> Set(Leaving, Down, Removed),
|
|
|
|
|
|
Leaving -> Set(Exiting, Down, Removed),
|
|
|
|
|
|
Down -> Set(Removed),
|
|
|
|
|
|
Exiting -> Set(Removed, Down),
|
|
|
|
|
|
Removed -> Set.empty[MemberStatus])
|
2013-01-09 01:47:48 +01:00
|
|
|
|
}
|