2012-08-19 20:15:22 +02:00
|
|
|
/**
|
2013-01-09 01:47:48 +01:00
|
|
|
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
package akka.cluster
|
|
|
|
|
|
|
|
|
|
import language.postfixOps
|
2012-11-15 12:33:11 +01:00
|
|
|
import scala.collection.immutable
|
2012-11-27 18:07:37 +01:00
|
|
|
import scala.collection.immutable.{ VectorBuilder, SortedSet }
|
2012-08-19 20:15:22 +02:00
|
|
|
import akka.actor.{ Actor, ActorLogging, ActorRef, Address }
|
|
|
|
|
import akka.cluster.ClusterEvent._
|
|
|
|
|
import akka.cluster.MemberStatus._
|
|
|
|
|
import akka.event.EventStream
|
2012-09-03 20:37:33 +02:00
|
|
|
import akka.actor.AddressTerminated
|
2012-11-08 18:49:54 +01:00
|
|
|
import java.lang.Iterable
|
2012-11-15 12:33:11 +01:00
|
|
|
import akka.japi.Util.immutableSeq
|
|
|
|
|
import akka.util.Collections.EmptyImmutableSeq
|
2012-08-19 20:15:22 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Domain events published to the event bus.
|
|
|
|
|
* Subscribe with:
|
|
|
|
|
* {{{
|
|
|
|
|
* Cluster(system).subscribe(actorRef, classOf[ClusterDomainEvent])
|
|
|
|
|
* }}}
|
|
|
|
|
*/
|
|
|
|
|
object ClusterEvent {
|
|
|
|
|
/**
|
|
|
|
|
* Marker interface for cluster domain events.
|
|
|
|
|
*/
|
|
|
|
|
sealed trait ClusterDomainEvent
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Current snapshot state of the cluster. Sent to new subscriber.
|
|
|
|
|
*/
|
|
|
|
|
case class CurrentClusterState(
|
2012-11-15 12:33:11 +01:00
|
|
|
members: immutable.SortedSet[Member] = immutable.SortedSet.empty,
|
2012-08-19 20:15:22 +02:00
|
|
|
unreachable: Set[Member] = Set.empty,
|
|
|
|
|
seenBy: Set[Address] = Set.empty,
|
2012-10-04 10:55:22 +02:00
|
|
|
leader: Option[Address] = None) extends ClusterDomainEvent {
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
* Java API: get current member list.
|
2012-10-04 10:55:22 +02:00
|
|
|
*/
|
|
|
|
|
def getMembers: java.lang.Iterable[Member] = {
|
|
|
|
|
import scala.collection.JavaConverters._
|
|
|
|
|
members.asJava
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
* Java API: get current unreachable set.
|
2012-10-04 10:55:22 +02:00
|
|
|
*/
|
2012-11-15 12:33:11 +01:00
|
|
|
def getUnreachable: java.util.Set[Member] =
|
|
|
|
|
scala.collection.JavaConverters.setAsJavaSetConverter(unreachable).asJava
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
* Java API: get current “seen-by” set.
|
2012-10-04 10:55:22 +02:00
|
|
|
*/
|
2012-11-15 12:33:11 +01:00
|
|
|
def getSeenBy: java.util.Set[Address] =
|
|
|
|
|
scala.collection.JavaConverters.setAsJavaSetConverter(seenBy).asJava
|
2012-10-04 10:55:22 +02:00
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
* Java API: get address of current leader, or null if none
|
2012-10-04 10:55:22 +02:00
|
|
|
*/
|
|
|
|
|
def getLeader: Address = leader orNull
|
|
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* Marker interface for membership events.
|
|
|
|
|
* Only published after convergence, when all members have seen current
|
|
|
|
|
* state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
sealed trait MemberEvent extends ClusterDomainEvent {
|
|
|
|
|
def member: Member
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* A new member joined the cluster.
|
|
|
|
|
* Only published after convergence, when all members have seen current
|
|
|
|
|
* state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
case class MemberJoined(member: Member) extends MemberEvent {
|
|
|
|
|
if (member.status != Joining) throw new IllegalArgumentException("Expected Joining status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* Member status changed to Up.
|
|
|
|
|
* Only published after convergence, when all members have seen current
|
|
|
|
|
* state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
case class MemberUp(member: Member) extends MemberEvent {
|
|
|
|
|
if (member.status != Up) throw new IllegalArgumentException("Expected Up status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* Member status changed to Leaving.
|
|
|
|
|
* Only published after convergence, when all members have seen current
|
|
|
|
|
* state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
case class MemberLeft(member: Member) extends MemberEvent {
|
|
|
|
|
if (member.status != Leaving) throw new IllegalArgumentException("Expected Leaving status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* Member status changed to Exiting.
|
|
|
|
|
* Only published after convergence, when all members have seen current
|
|
|
|
|
* state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
case class MemberExited(member: Member) extends MemberEvent {
|
|
|
|
|
if (member.status != Exiting) throw new IllegalArgumentException("Expected Exiting status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* Member status changed to Down.
|
|
|
|
|
* Only published after convergence, when all members have seen current
|
|
|
|
|
* state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
case class MemberDowned(member: Member) extends MemberEvent {
|
|
|
|
|
if (member.status != Down) throw new IllegalArgumentException("Expected Down status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 17:35:56 +01:00
|
|
|
* Member completely removed from the cluster. Only published after convergence,
|
|
|
|
|
* when all other members have seen the state.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
|
|
|
|
case class MemberRemoved(member: Member) extends MemberEvent {
|
|
|
|
|
if (member.status != Removed) throw new IllegalArgumentException("Expected Removed status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-14 17:35:56 +01:00
|
|
|
/**
|
|
|
|
|
* Current snapshot state of the cluster. Sent to new subscriber of
|
|
|
|
|
* [akka.cluster.ClusterEvent.InstantMemberEvent].
|
|
|
|
|
*/
|
|
|
|
|
case class InstantClusterState(members: immutable.SortedSet[Member] = immutable.SortedSet.empty)
|
|
|
|
|
extends ClusterDomainEvent {
|
|
|
|
|
|
|
|
|
|
/**
|
2013-03-07 09:05:55 +01:00
|
|
|
* Java API: get current member list
|
2013-01-14 17:35:56 +01:00
|
|
|
*/
|
|
|
|
|
def getMembers: java.lang.Iterable[Member] = {
|
|
|
|
|
import scala.collection.JavaConverters._
|
|
|
|
|
members.asJava
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-01-14 19:32:52 +01:00
|
|
|
* Marker interface for membership events published immediately when
|
|
|
|
|
* it happened. All other members might not have seen the state.
|
2013-01-14 17:35:56 +01:00
|
|
|
*/
|
|
|
|
|
sealed trait InstantMemberEvent extends ClusterDomainEvent {
|
|
|
|
|
def member: Member
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A new member joined the cluster. Published immediately when it happened.
|
|
|
|
|
* All other members might not have seen the state.
|
|
|
|
|
*/
|
|
|
|
|
case class InstantMemberJoined(member: Member) extends InstantMemberEvent {
|
|
|
|
|
if (member.status != Joining) throw new IllegalArgumentException("Expected Joining status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Member status changed to Up. Published immediately when it happened.
|
|
|
|
|
* All other members might not have seen the state.
|
|
|
|
|
*/
|
|
|
|
|
case class InstantMemberUp(member: Member) extends InstantMemberEvent {
|
|
|
|
|
if (member.status != Up) throw new IllegalArgumentException("Expected Up status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Member status changed to Leaving. Published immediately when it happened.
|
|
|
|
|
* All other members might not have seen the state.
|
|
|
|
|
*/
|
|
|
|
|
case class InstantMemberLeft(member: Member) extends InstantMemberEvent {
|
|
|
|
|
if (member.status != Leaving) throw new IllegalArgumentException("Expected Leaving status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Member status changed to Exiting. Published immediately when it happened.
|
|
|
|
|
* All other members might not have seen the state.
|
|
|
|
|
*/
|
|
|
|
|
case class InstantMemberExited(member: Member) extends InstantMemberEvent {
|
|
|
|
|
if (member.status != Exiting) throw new IllegalArgumentException("Expected Exiting status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Member status changed to Down. Published immediately when it happened.
|
|
|
|
|
* All other members might not have seen the state.
|
|
|
|
|
*/
|
|
|
|
|
case class InstantMemberDowned(member: Member) extends InstantMemberEvent {
|
|
|
|
|
if (member.status != Down) throw new IllegalArgumentException("Expected Down status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Member completely removed from the cluster. Published immediately when it happened.
|
|
|
|
|
* All other members might not have seen the state.
|
|
|
|
|
*/
|
|
|
|
|
case class InstantMemberRemoved(member: Member) extends InstantMemberEvent {
|
|
|
|
|
if (member.status != Removed) throw new IllegalArgumentException("Expected Removed status, got: " + member)
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 20:15:22 +02:00
|
|
|
/**
|
2012-09-18 14:19:38 +02:00
|
|
|
* Leader of the cluster members changed. Only published after convergence.
|
2012-08-19 20:15:22 +02:00
|
|
|
*/
|
2012-10-04 10:55:22 +02:00
|
|
|
case class LeaderChanged(leader: Option[Address]) extends ClusterDomainEvent {
|
|
|
|
|
/**
|
|
|
|
|
* Java API
|
|
|
|
|
* @return address of current leader, or null if none
|
|
|
|
|
*/
|
|
|
|
|
def getLeader: Address = leader orNull
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-11-27 18:07:37 +01:00
|
|
|
* A member is considered as unreachable by the failure detector.
|
|
|
|
|
*/
|
|
|
|
|
case class UnreachableMember(member: Member) extends ClusterDomainEvent
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
2012-10-04 10:55:22 +02:00
|
|
|
*
|
2012-11-08 18:49:54 +01:00
|
|
|
* Current snapshot of cluster node metrics. Published to subscribers.
|
2012-10-04 10:55:22 +02:00
|
|
|
*/
|
2012-11-08 18:49:54 +01:00
|
|
|
case class ClusterMetricsChanged(nodeMetrics: Set[NodeMetrics]) extends ClusterDomainEvent {
|
|
|
|
|
/**
|
|
|
|
|
* Java API
|
|
|
|
|
*/
|
2012-11-15 12:33:11 +01:00
|
|
|
def getNodeMetrics: java.lang.Iterable[NodeMetrics] =
|
|
|
|
|
scala.collection.JavaConverters.asJavaIterableConverter(nodeMetrics).asJava
|
2012-11-08 18:49:54 +01:00
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
* The nodes that have seen current version of the Gossip.
|
|
|
|
|
*/
|
|
|
|
|
private[cluster] case class SeenChanged(convergence: Boolean, seenBy: Set[Address]) extends ClusterDomainEvent
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[cluster] case class CurrentInternalStats(stats: ClusterStats) extends ClusterDomainEvent
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
2012-11-27 18:07:37 +01:00
|
|
|
private[cluster] def diffUnreachable(oldGossip: Gossip, newGossip: Gossip): immutable.Seq[UnreachableMember] =
|
|
|
|
|
if (newGossip eq oldGossip) Nil
|
|
|
|
|
else {
|
|
|
|
|
val newUnreachable = newGossip.overview.unreachable -- oldGossip.overview.unreachable
|
|
|
|
|
val unreachableEvents = newUnreachable map UnreachableMember
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
immutable.Seq.empty ++ unreachableEvents
|
2012-08-19 20:15:22 +02:00
|
|
|
}
|
|
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API.
|
|
|
|
|
*/
|
|
|
|
|
private[cluster] def diffMemberEvents(oldGossip: Gossip, newGossip: Gossip): immutable.Seq[MemberEvent] =
|
|
|
|
|
if (newGossip eq oldGossip) Nil
|
|
|
|
|
else {
|
|
|
|
|
val newMembers = newGossip.members -- oldGossip.members
|
|
|
|
|
val membersGroupedByAddress = List(newGossip.members, oldGossip.members).flatten.groupBy(_.address)
|
|
|
|
|
val changedMembers = membersGroupedByAddress collect {
|
|
|
|
|
case (_, newMember :: oldMember :: Nil) if newMember.status != oldMember.status ⇒ newMember
|
|
|
|
|
}
|
|
|
|
|
val memberEvents = (newMembers ++ changedMembers) map { m ⇒
|
|
|
|
|
m.status match {
|
|
|
|
|
case Joining ⇒ MemberJoined(m)
|
|
|
|
|
case Up ⇒ MemberUp(m)
|
|
|
|
|
case Leaving ⇒ MemberLeft(m)
|
|
|
|
|
case Exiting ⇒ MemberExited(m)
|
|
|
|
|
case _ ⇒ throw new IllegalStateException("Unexpected member status: " + m)
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
val allNewUnreachable = newGossip.overview.unreachable -- oldGossip.overview.unreachable
|
|
|
|
|
val newDowned = allNewUnreachable filter { _.status == Down }
|
|
|
|
|
val downedEvents = newDowned map MemberDowned
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
val unreachableGroupedByAddress =
|
|
|
|
|
List(newGossip.overview.unreachable, oldGossip.overview.unreachable).flatten.groupBy(_.address)
|
|
|
|
|
val unreachableDownMembers = unreachableGroupedByAddress collect {
|
|
|
|
|
case (_, newMember :: oldMember :: Nil) if newMember.status == Down && newMember.status != oldMember.status ⇒
|
|
|
|
|
newMember
|
|
|
|
|
}
|
|
|
|
|
val unreachableDownedEvents = unreachableDownMembers map MemberDowned
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2013-03-05 15:32:13 +01:00
|
|
|
val removedMembers = (oldGossip.members -- newGossip.members -- newGossip.overview.unreachable) ++
|
|
|
|
|
(oldGossip.overview.unreachable -- newGossip.overview.unreachable)
|
|
|
|
|
val removedEvents = removedMembers.map(m ⇒ MemberRemoved(m.copy(status = Removed)))
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
(new VectorBuilder[MemberEvent]() ++= memberEvents ++= downedEvents ++= unreachableDownedEvents
|
|
|
|
|
++= removedEvents).result()
|
|
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2013-01-14 17:35:56 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[cluster] def convertToInstantMemberEvents(memberEvents: immutable.Seq[MemberEvent]): immutable.Seq[InstantMemberEvent] =
|
2013-01-14 19:32:52 +01:00
|
|
|
memberEvents map {
|
|
|
|
|
case MemberJoined(m) ⇒ InstantMemberJoined(m)
|
|
|
|
|
case MemberUp(m) ⇒ InstantMemberUp(m)
|
|
|
|
|
case MemberDowned(m) ⇒ InstantMemberDowned(m)
|
|
|
|
|
case MemberLeft(m) ⇒ InstantMemberLeft(m)
|
|
|
|
|
case MemberExited(m) ⇒ InstantMemberExited(m)
|
|
|
|
|
case MemberRemoved(m) ⇒ InstantMemberRemoved(m)
|
2013-01-14 17:35:56 +01:00
|
|
|
}
|
|
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[cluster] def diffLeader(oldGossip: Gossip, newGossip: Gossip): immutable.Seq[LeaderChanged] =
|
|
|
|
|
if (newGossip.leader != oldGossip.leader) List(LeaderChanged(newGossip.leader))
|
|
|
|
|
else Nil
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[cluster] def diffSeen(oldGossip: Gossip, newGossip: Gossip): immutable.Seq[SeenChanged] =
|
|
|
|
|
if (newGossip eq oldGossip) Nil
|
|
|
|
|
else {
|
|
|
|
|
val newConvergence = newGossip.convergence
|
|
|
|
|
val newSeenBy = newGossip.seenBy
|
|
|
|
|
if (newConvergence != oldGossip.convergence || newSeenBy != oldGossip.seenBy)
|
|
|
|
|
List(SeenChanged(newConvergence, newSeenBy))
|
|
|
|
|
else Nil
|
|
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API.
|
|
|
|
|
* Responsible for domain event subscriptions and publishing of
|
|
|
|
|
* domain events to event bus.
|
|
|
|
|
*/
|
2012-09-06 21:48:40 +02:00
|
|
|
private[cluster] final class ClusterDomainEventPublisher extends Actor with ActorLogging {
|
2012-08-19 20:15:22 +02:00
|
|
|
import InternalClusterAction._
|
|
|
|
|
|
2013-01-04 16:39:48 +01:00
|
|
|
var latestGossip: Gossip = Gossip.empty
|
|
|
|
|
var latestConvergedGossip: Gossip = Gossip.empty
|
2013-02-08 09:17:55 +01:00
|
|
|
var bufferedEvents: immutable.IndexedSeq[ClusterDomainEvent] = Vector.empty
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2013-02-11 10:40:01 +01:00
|
|
|
override def preRestart(reason: Throwable, message: Option[Any]) {
|
|
|
|
|
// don't postStop when restarted, no children to stop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def postStop(): Unit = {
|
|
|
|
|
// publish the final removed state before shutting down
|
|
|
|
|
publishChanges(Gossip.empty)
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 20:15:22 +02:00
|
|
|
def receive = {
|
2012-11-27 18:07:37 +01:00
|
|
|
case PublishChanges(newGossip) ⇒ publishChanges(newGossip)
|
2012-08-19 20:15:22 +02:00
|
|
|
case currentStats: CurrentInternalStats ⇒ publishInternalStats(currentStats)
|
2012-09-12 09:23:02 +02:00
|
|
|
case PublishCurrentClusterState(receiver) ⇒ publishCurrentClusterState(receiver)
|
2012-08-19 20:15:22 +02:00
|
|
|
case Subscribe(subscriber, to) ⇒ subscribe(subscriber, to)
|
2012-09-28 13:09:36 +02:00
|
|
|
case Unsubscribe(subscriber, to) ⇒ unsubscribe(subscriber, to)
|
2012-10-02 16:41:03 +02:00
|
|
|
case PublishEvent(event) ⇒ publish(event)
|
2012-11-27 18:07:37 +01:00
|
|
|
case PublishStart ⇒ publishStart()
|
2012-08-19 20:15:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def eventStream: EventStream = context.system.eventStream
|
|
|
|
|
|
2013-01-14 19:32:52 +01:00
|
|
|
/**
|
|
|
|
|
* The current snapshot state that is a mix of converged and latest gossip
|
|
|
|
|
* to mimic what you would have seen if you where listening to the events.
|
|
|
|
|
*/
|
2012-09-12 09:23:02 +02:00
|
|
|
def publishCurrentClusterState(receiver: Option[ActorRef]): Unit = {
|
|
|
|
|
val state = CurrentClusterState(
|
2012-11-27 18:07:37 +01:00
|
|
|
members = latestConvergedGossip.members,
|
2012-08-19 20:15:22 +02:00
|
|
|
unreachable = latestGossip.overview.unreachable,
|
|
|
|
|
seenBy = latestGossip.seenBy,
|
2012-11-27 18:07:37 +01:00
|
|
|
leader = latestConvergedGossip.leader)
|
2012-09-12 09:23:02 +02:00
|
|
|
receiver match {
|
|
|
|
|
case Some(ref) ⇒ ref ! state
|
2012-10-02 16:41:03 +02:00
|
|
|
case None ⇒ publish(state)
|
2012-09-12 09:23:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-14 19:32:52 +01:00
|
|
|
/**
|
|
|
|
|
* Publish the snapshot state that is based on latest gossip to mimic what you
|
|
|
|
|
* would have seen if you where listening to the InstantMemberEvent stream.
|
|
|
|
|
*/
|
|
|
|
|
def publishInstantClusterState(receiver: ActorRef): Unit =
|
2013-01-14 17:35:56 +01:00
|
|
|
receiver ! InstantClusterState(members = latestGossip.members)
|
|
|
|
|
|
2012-09-12 09:23:02 +02:00
|
|
|
def subscribe(subscriber: ActorRef, to: Class[_]): Unit = {
|
2013-01-14 19:32:52 +01:00
|
|
|
val isInstantMemberEvent = classOf[InstantMemberEvent].isAssignableFrom(to)
|
|
|
|
|
if (classOf[ClusterDomainEvent] == to || isInstantMemberEvent)
|
2013-01-14 17:35:56 +01:00
|
|
|
publishInstantClusterState(subscriber)
|
2013-01-14 19:32:52 +01:00
|
|
|
if (!isInstantMemberEvent)
|
2013-01-14 17:35:56 +01:00
|
|
|
publishCurrentClusterState(Some(subscriber))
|
|
|
|
|
|
2012-08-19 20:15:22 +02:00
|
|
|
eventStream.subscribe(subscriber, to)
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-28 13:09:36 +02:00
|
|
|
def unsubscribe(subscriber: ActorRef, to: Option[Class[_]]): Unit = to match {
|
|
|
|
|
case None ⇒ eventStream.unsubscribe(subscriber)
|
|
|
|
|
case Some(c) ⇒ eventStream.unsubscribe(subscriber, c)
|
|
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
|
2012-11-27 18:07:37 +01:00
|
|
|
def publishChanges(newGossip: Gossip): Unit = {
|
|
|
|
|
val oldGossip = latestGossip
|
2012-08-19 20:15:22 +02:00
|
|
|
// keep the latestGossip to be sent to new subscribers
|
|
|
|
|
latestGossip = newGossip
|
2012-11-27 18:07:37 +01:00
|
|
|
// first publish the diffUnreachable between the last two gossips
|
2012-12-12 11:49:20 +01:00
|
|
|
diffUnreachable(oldGossip, newGossip) foreach publish
|
2013-01-14 17:35:56 +01:00
|
|
|
val newMemberEvents = diffMemberEvents(oldGossip, newGossip)
|
|
|
|
|
convertToInstantMemberEvents(newMemberEvents) foreach publish
|
2012-11-27 18:07:37 +01:00
|
|
|
// buffer up the MemberEvents waiting for convergence
|
2013-02-08 09:17:55 +01:00
|
|
|
bufferedEvents ++= newMemberEvents
|
|
|
|
|
// buffer up the LeaderChanged waiting for convergence
|
|
|
|
|
bufferedEvents ++= diffLeader(oldGossip, newGossip)
|
|
|
|
|
// if we have convergence then publish the MemberEvents and LeaderChanged
|
2012-11-27 18:07:37 +01:00
|
|
|
if (newGossip.convergence) {
|
|
|
|
|
val previousConvergedGossip = latestConvergedGossip
|
|
|
|
|
latestConvergedGossip = newGossip
|
2013-02-08 09:17:55 +01:00
|
|
|
bufferedEvents foreach { event ⇒
|
2012-12-12 11:49:20 +01:00
|
|
|
event match {
|
2013-03-05 15:32:13 +01:00
|
|
|
case m: MemberEvent if m.isInstanceOf[MemberRemoved] ⇒
|
2012-12-12 11:49:20 +01:00
|
|
|
publish(event)
|
|
|
|
|
// notify DeathWatch about downed node
|
|
|
|
|
publish(AddressTerminated(m.member.address))
|
|
|
|
|
case _ ⇒ publish(event)
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-08 09:17:55 +01:00
|
|
|
bufferedEvents = Vector.empty
|
2012-09-03 20:37:33 +02:00
|
|
|
}
|
2012-11-27 18:07:37 +01:00
|
|
|
// publish internal SeenState for testing purposes
|
|
|
|
|
diffSeen(oldGossip, newGossip) foreach publish
|
2012-08-19 20:15:22 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-02 16:41:03 +02:00
|
|
|
def publishInternalStats(currentStats: CurrentInternalStats): Unit = publish(currentStats)
|
|
|
|
|
|
|
|
|
|
def publish(event: AnyRef): Unit = eventStream publish event
|
2012-11-27 18:07:37 +01:00
|
|
|
|
2013-01-04 16:39:48 +01:00
|
|
|
def publishStart(): Unit =
|
|
|
|
|
if ((latestGossip ne Gossip.empty) || (latestConvergedGossip ne Gossip.empty)) {
|
|
|
|
|
clearState()
|
|
|
|
|
publishCurrentClusterState(None)
|
|
|
|
|
}
|
2012-11-27 18:07:37 +01:00
|
|
|
|
|
|
|
|
def clearState(): Unit = {
|
2013-01-04 16:39:48 +01:00
|
|
|
latestGossip = Gossip.empty
|
|
|
|
|
latestConvergedGossip = Gossip.empty
|
2012-11-27 18:07:37 +01:00
|
|
|
}
|
2012-08-19 20:15:22 +02:00
|
|
|
}
|