2018-10-29 17:19:37 +08:00
|
|
|
|
/*
|
2019-01-02 18:55:26 +08:00
|
|
|
|
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
|
2012-07-05 13:55:08 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
package akka.cluster
|
|
|
|
|
|
|
|
|
|
|
|
import akka.actor.Address
|
|
|
|
|
|
import MemberStatus._
|
2017-07-04 09:09:40 +01:00
|
|
|
|
import akka.annotation.InternalApi
|
2017-07-04 17:11:21 +02:00
|
|
|
|
import akka.cluster.ClusterSettings.DataCenter
|
2012-07-05 13:55:08 +02:00
|
|
|
|
|
2016-09-26 15:34:59 +02:00
|
|
|
|
import scala.runtime.AbstractFunction2
|
|
|
|
|
|
|
2012-07-05 13:55:08 +02:00
|
|
|
|
/**
|
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)
|
2019-03-13 10:56:20 +01:00
|
|
|
|
class Member private[cluster] (
|
|
|
|
|
|
val uniqueAddress: UniqueAddress,
|
|
|
|
|
|
private[cluster] val upNumber: Int, // INTERNAL API
|
|
|
|
|
|
val status: MemberStatus,
|
|
|
|
|
|
val roles: Set[String])
|
2019-03-11 10:38:24 +01:00
|
|
|
|
extends Serializable {
|
|
|
|
|
|
|
|
|
|
|
|
lazy val dataCenter: DataCenter = roles
|
|
|
|
|
|
.find(_.startsWith(ClusterSettings.DcRolePrefix))
|
2017-07-04 17:11:21 +02:00
|
|
|
|
.getOrElse(throw new IllegalStateException("DataCenter undefined, should not be possible"))
|
|
|
|
|
|
.substring(ClusterSettings.DcRolePrefix.length)
|
2017-06-26 14:32:57 +02:00
|
|
|
|
|
2013-04-11 09:18:12 +02:00
|
|
|
|
def address: Address = uniqueAddress.address
|
|
|
|
|
|
|
|
|
|
|
|
override def hashCode = uniqueAddress.##
|
2013-03-14 20:32:43 +01:00
|
|
|
|
override def equals(other: Any) = other match {
|
2019-02-09 15:25:39 +01:00
|
|
|
|
case m: Member => uniqueAddress == m.uniqueAddress
|
|
|
|
|
|
case _ => false
|
2013-03-14 20:32:43 +01:00
|
|
|
|
}
|
2017-07-04 09:09:40 +01:00
|
|
|
|
override def toString =
|
2017-07-04 17:11:21 +02:00
|
|
|
|
if (dataCenter == ClusterSettings.DefaultDataCenter)
|
2017-07-04 09:09:40 +01:00
|
|
|
|
s"Member(address = $address, status = $status)"
|
|
|
|
|
|
else
|
2017-07-04 17:11:21 +02:00
|
|
|
|
s"Member(address = $address, dataCenter = $dataCenter, status = $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
|
|
|
|
|
2013-04-28 22:05:40 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Is this member older, has been part of cluster longer, than another
|
|
|
|
|
|
* member. It is only correct when comparing two existing members in a
|
|
|
|
|
|
* cluster. A member that joined after removal of another member may be
|
2017-09-25 11:50:28 +02:00
|
|
|
|
* considered older than the removed member.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Note that it only makes sense to compare with other members of
|
|
|
|
|
|
* same data center (upNumber has a higher risk of being reused across data centers).
|
|
|
|
|
|
* To avoid mistakes of comparing members of different data centers this
|
|
|
|
|
|
* method will throw `IllegalArgumentException` if the members belong
|
|
|
|
|
|
* to different data centers.
|
2013-04-28 22:05:40 +02:00
|
|
|
|
*/
|
2017-09-25 11:50:28 +02:00
|
|
|
|
@throws[IllegalArgumentException]("if members from different data centers")
|
|
|
|
|
|
def isOlderThan(other: Member): Boolean = {
|
|
|
|
|
|
if (dataCenter != other.dataCenter)
|
|
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
|
|
"Comparing members of different data centers with isOlderThan is not allowed. " +
|
2019-03-11 10:38:24 +01:00
|
|
|
|
s"[$this] vs. [$other]")
|
2015-10-21 07:53:12 +02:00
|
|
|
|
if (upNumber == other.upNumber)
|
|
|
|
|
|
Member.addressOrdering.compare(address, other.address) < 0
|
|
|
|
|
|
else
|
|
|
|
|
|
upNumber < other.upNumber
|
2017-09-25 11:50:28 +02:00
|
|
|
|
}
|
2013-04-28 22:05:40 +02:00
|
|
|
|
|
2013-04-05 12:38:09 +02:00
|
|
|
|
def copy(status: MemberStatus): Member = {
|
|
|
|
|
|
val oldStatus = this.status
|
|
|
|
|
|
if (status == oldStatus) this
|
|
|
|
|
|
else {
|
2019-03-11 10:38:24 +01:00
|
|
|
|
require(allowedTransitions(oldStatus)(status), s"Invalid member status transition [ ${this} -> ${status}]")
|
2013-04-28 22:05:40 +02:00
|
|
|
|
new Member(uniqueAddress, upNumber, status, roles)
|
2013-04-05 12:38:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-04-28 22:05:40 +02:00
|
|
|
|
|
|
|
|
|
|
def copyUp(upNumber: Int): Member = {
|
|
|
|
|
|
new Member(uniqueAddress, upNumber, status, roles).copy(Up)
|
|
|
|
|
|
}
|
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-11 09:18:12 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
|
* Create a new member with status Joining.
|
|
|
|
|
|
*/
|
2017-09-21 17:58:29 +02:00
|
|
|
|
private[akka] def apply(uniqueAddress: UniqueAddress, roles: Set[String]): Member =
|
2013-04-28 22:05:40 +02:00
|
|
|
|
new Member(uniqueAddress, Int.MaxValue, Joining, roles)
|
2013-04-11 09:18:12 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
|
*/
|
2017-07-04 09:09:40 +01:00
|
|
|
|
private[cluster] def removed(node: UniqueAddress): Member =
|
2017-07-04 17:11:21 +02:00
|
|
|
|
new Member(node, Int.MaxValue, Removed, Set(ClusterSettings.DcRolePrefix + "-N/A"))
|
2013-04-05 12:38:09 +02:00
|
|
|
|
|
2012-07-05 13:55:08 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* `Address` ordering type class, sorts addresses by host and port.
|
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
|
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
|
2013-09-11 14:46:08 +02:00
|
|
|
|
if (a eq b) false
|
|
|
|
|
|
else if (a.host != b.host) a.host.getOrElse("").compareTo(b.host.getOrElse("")) < 0
|
2012-07-05 13:55:08 +02:00
|
|
|
|
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).
|
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
|
private[cluster] val leaderStatusOrdering: Ordering[Member] = Ordering.fromLessThan[Member] { (a, b) =>
|
2013-03-06 16:39:22 +01:00
|
|
|
|
(a.status, b.status) match {
|
2019-02-09 15:25:39 +01:00
|
|
|
|
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 (WeaklyUp, _) => false
|
|
|
|
|
|
case (_, WeaklyUp) => true
|
|
|
|
|
|
case _ => ordering.compare(a, b) <= 0
|
2013-03-06 16:39:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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] {
|
2013-04-11 09:18:12 +02:00
|
|
|
|
def compare(a: Member, b: Member): Int = {
|
2019-03-11 10:38:24 +01:00
|
|
|
|
a.uniqueAddress.compare(b.uniqueAddress)
|
2013-04-11 09:18:12 +02:00
|
|
|
|
}
|
2012-07-05 13:55:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-10-21 07:53:12 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Sort members by age, i.e. using [[Member#isOlderThan]].
|
2017-09-25 11:50:28 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Note that it only makes sense to compare with other members of
|
|
|
|
|
|
* same data center. To avoid mistakes of comparing members of different
|
|
|
|
|
|
* data centers it will throw `IllegalArgumentException` if the
|
|
|
|
|
|
* members belong to different data centers.
|
2015-10-21 07:53:12 +02:00
|
|
|
|
*/
|
2019-03-11 10:38:24 +01:00
|
|
|
|
val ageOrdering: Ordering[Member] = Ordering.fromLessThan[Member] { (a, b) =>
|
|
|
|
|
|
a.isOlderThan(b)
|
2015-10-21 07:53:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-04 09:09:40 +01:00
|
|
|
|
@deprecated("Was accidentally made a public API, internal", since = "2.5.4")
|
|
|
|
|
|
def pickHighestPriority(a: Set[Member], b: Set[Member]): Set[Member] =
|
|
|
|
|
|
pickHighestPriority(a, b, Map.empty)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* INTERNAL API.
|
|
|
|
|
|
*/
|
|
|
|
|
|
@InternalApi
|
2019-03-13 10:56:20 +01:00
|
|
|
|
private[akka] def pickHighestPriority(
|
|
|
|
|
|
a: Set[Member],
|
|
|
|
|
|
b: Set[Member],
|
|
|
|
|
|
tombstones: Map[UniqueAddress, Long]): Set[Member] = {
|
2012-07-05 13:55:08 +02:00
|
|
|
|
// group all members by Address => Seq[Member]
|
2013-04-11 09:18:12 +02:00
|
|
|
|
val groupedByAddress = (a.toSeq ++ b.toSeq).groupBy(_.uniqueAddress)
|
2012-07-05 13:55:08 +02:00
|
|
|
|
// pick highest MemberStatus
|
2017-07-04 09:09:40 +01:00
|
|
|
|
groupedByAddress.foldLeft(Member.none) {
|
2019-02-09 15:25:39 +01:00
|
|
|
|
case (acc, (_, members)) =>
|
2013-09-12 17:19:53 +02:00
|
|
|
|
if (members.size == 2) acc + members.reduceLeft(highestPriorityOf)
|
|
|
|
|
|
else {
|
|
|
|
|
|
val m = members.head
|
2019-03-11 10:38:24 +01:00
|
|
|
|
if (tombstones.contains(m.uniqueAddress) || MembershipState.removeUnreachableWithMemberStatus(m.status))
|
|
|
|
|
|
acc // removed
|
2013-09-12 17:19:53 +02:00
|
|
|
|
else acc + m
|
|
|
|
|
|
}
|
2012-07-05 13:55:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Picks the Member with the highest "priority" MemberStatus.
|
|
|
|
|
|
*/
|
2015-10-21 07:53:12 +02:00
|
|
|
|
def highestPriorityOf(m1: Member, m2: Member): Member = {
|
|
|
|
|
|
if (m1.status == m2.status)
|
|
|
|
|
|
// preserve the oldest in case of different upNumber
|
|
|
|
|
|
if (m1.isOlderThan(m2)) m1 else m2
|
2019-03-11 10:38:24 +01:00
|
|
|
|
else
|
|
|
|
|
|
(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 (Joining, _) => m2
|
|
|
|
|
|
case (_, Joining) => m1
|
|
|
|
|
|
case (WeaklyUp, _) => m2
|
|
|
|
|
|
case (_, WeaklyUp) => m1
|
|
|
|
|
|
case (Up, Up) => m1
|
|
|
|
|
|
}
|
2012-07-05 13:55:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Defines the current status of a cluster member node
|
|
|
|
|
|
*
|
2016-02-03 09:16:15 +10:00
|
|
|
|
* Can be one of: Joining, WeaklyUp, Up, Leaving, Exiting and Down and Removed.
|
2012-07-05 13:55:08 +02:00
|
|
|
|
*/
|
2016-02-03 09:16:15 +10:00
|
|
|
|
sealed 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
|
2015-08-25 17:20:05 -05:00
|
|
|
|
@SerialVersionUID(1L) case object WeaklyUp extends MemberStatus
|
2013-04-04 17:56:29 +02:00
|
|
|
|
@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
|
|
|
|
|
2015-09-04 12:38:49 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Java API: retrieve the “weaklyUp” status singleton.
|
|
|
|
|
|
*/
|
|
|
|
|
|
def weaklyUp: MemberStatus = WeaklyUp
|
|
|
|
|
|
|
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]] =
|
2019-03-13 10:56:20 +01:00
|
|
|
|
Map(
|
|
|
|
|
|
Joining -> Set(WeaklyUp, Up, Leaving, Down, Removed),
|
|
|
|
|
|
WeaklyUp -> Set(Up, Leaving, 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
|
|
|
|
}
|
2013-04-11 09:18:12 +02:00
|
|
|
|
|
2016-09-26 15:34:59 +02:00
|
|
|
|
object UniqueAddress extends AbstractFunction2[Address, Int, UniqueAddress] {
|
|
|
|
|
|
|
|
|
|
|
|
// for binary compatibility
|
|
|
|
|
|
@deprecated("Use Long UID apply instead", since = "2.4.11")
|
|
|
|
|
|
def apply(address: Address, uid: Int) = new UniqueAddress(address, uid.toLong)
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-11 09:18:12 +02:00
|
|
|
|
/**
|
2014-06-05 13:25:42 +02:00
|
|
|
|
* Member identifier consisting of address and random `uid`.
|
|
|
|
|
|
* The `uid` is needed to be able to distinguish different
|
|
|
|
|
|
* incarnations of a member with same hostname and port.
|
2013-04-11 09:18:12 +02:00
|
|
|
|
*/
|
|
|
|
|
|
@SerialVersionUID(1L)
|
2016-09-26 15:34:59 +02:00
|
|
|
|
final case class UniqueAddress(address: Address, longUid: Long) extends Ordered[UniqueAddress] {
|
|
|
|
|
|
|
|
|
|
|
|
override def hashCode = java.lang.Long.hashCode(longUid)
|
2013-06-13 15:43:37 -04:00
|
|
|
|
|
2013-08-27 15:14:53 +02:00
|
|
|
|
def compare(that: UniqueAddress): Int = {
|
2013-06-13 15:43:37 -04:00
|
|
|
|
val result = Member.addressOrdering.compare(this.address, that.address)
|
2016-09-26 15:34:59 +02:00
|
|
|
|
if (result == 0) if (this.longUid < that.longUid) -1 else if (this.longUid == that.longUid) 0 else 1
|
2013-06-13 15:43:37 -04:00
|
|
|
|
else result
|
|
|
|
|
|
}
|
2016-09-26 15:34:59 +02:00
|
|
|
|
|
|
|
|
|
|
// for binary compatibility
|
|
|
|
|
|
|
|
|
|
|
|
@deprecated("Use Long UID constructor instead", since = "2.4.11")
|
|
|
|
|
|
def this(address: Address, uid: Int) = this(address, uid.toLong)
|
|
|
|
|
|
|
|
|
|
|
|
@deprecated("Use longUid instead", since = "2.4.11")
|
|
|
|
|
|
def uid = longUid.toInt
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* For binary compatibility
|
|
|
|
|
|
* Stops `copy(Address, Long)` copy from being generated, use `apply` instead.
|
|
|
|
|
|
*/
|
|
|
|
|
|
@deprecated("Use Long UID constructor instead", since = "2.4.11")
|
|
|
|
|
|
def copy(address: Address = address, uid: Int = uid) = new UniqueAddress(address, uid)
|
|
|
|
|
|
|
2017-01-04 17:37:10 +01:00
|
|
|
|
}
|