Publish InstantMemberEvent immediately, see #2803

This commit is contained in:
Patrik Nordwall 2013-01-14 17:35:56 +01:00
parent c321f735f3
commit d07f331e78
5 changed files with 201 additions and 61 deletions

View file

@ -68,54 +68,140 @@ object ClusterEvent {
}
/**
* Marker interface for member related events.
* Marker interface for membership events.
* Only published after convergence, when all members have seen current
* state.
*/
sealed trait MemberEvent extends ClusterDomainEvent {
def member: Member
}
/**
* A new member joined the cluster. Only published after convergence.
* A new member joined the cluster.
* Only published after convergence, when all members have seen current
* state.
*/
case class MemberJoined(member: Member) extends MemberEvent {
if (member.status != Joining) throw new IllegalArgumentException("Expected Joining status, got: " + member)
}
/**
* Member status changed to Up. Only published after convergence.
* Member status changed to Up.
* Only published after convergence, when all members have seen current
* state.
*/
case class MemberUp(member: Member) extends MemberEvent {
if (member.status != Up) throw new IllegalArgumentException("Expected Up status, got: " + member)
}
/**
* Member status changed to Leaving. Only published after convergence.
* Member status changed to Leaving.
* Only published after convergence, when all members have seen current
* state.
*/
case class MemberLeft(member: Member) extends MemberEvent {
if (member.status != Leaving) throw new IllegalArgumentException("Expected Leaving status, got: " + member)
}
/**
* Member status changed to Exiting. Only published after convergence.
* Member status changed to Exiting.
* Only published after convergence, when all members have seen current
* state.
*/
case class MemberExited(member: Member) extends MemberEvent {
if (member.status != Exiting) throw new IllegalArgumentException("Expected Exiting status, got: " + member)
}
/**
* Member status changed to Down. Only published after convergence.
* Member status changed to Down.
* Only published after convergence, when all members have seen current
* state.
*/
case class MemberDowned(member: Member) extends MemberEvent {
if (member.status != Down) throw new IllegalArgumentException("Expected Down status, got: " + member)
}
/**
* Member completely removed from the cluster. Only published after convergence.
* Member completely removed from the cluster. Only published after convergence,
* when all other members have seen the state.
*/
case class MemberRemoved(member: Member) extends MemberEvent {
if (member.status != Removed) throw new IllegalArgumentException("Expected Removed status, got: " + member)
}
/**
* 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 {
/**
* Java API
* Read only
*/
def getMembers: java.lang.Iterable[Member] = {
import scala.collection.JavaConverters._
members.asJava
}
}
/**
* Marker interface for membership events published immediately.
* All other members might not have seen the state.
*/
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)
}
/**
* Leader of the cluster members changed. Only published after convergence.
*/
@ -209,6 +295,21 @@ object ClusterEvent {
++= removedEvents).result()
}
/**
* INTERNAL API
*/
private[cluster] def convertToInstantMemberEvents(memberEvents: immutable.Seq[MemberEvent]): immutable.Seq[InstantMemberEvent] =
memberEvents map { event
event match {
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)
}
}
/**
* INTERNAL API
*/
@ -269,8 +370,21 @@ private[cluster] final class ClusterDomainEventPublisher extends Actor with Acto
}
}
def publishInstantClusterState(receiver: ActorRef): Unit = {
// The state is based on latest gossip to mimic what you
// would have seen if you where listening to the InstantMemberEvent stream.
receiver ! InstantClusterState(members = latestGossip.members)
}
def subscribe(subscriber: ActorRef, to: Class[_]): Unit = {
publishCurrentClusterState(Some(subscriber))
if (classOf[ClusterDomainEvent] == to) {
publishInstantClusterState(subscriber)
publishCurrentClusterState(Some(subscriber))
} else if (classOf[InstantMemberEvent].isAssignableFrom(to))
publishInstantClusterState(subscriber)
else
publishCurrentClusterState(Some(subscriber))
eventStream.subscribe(subscriber, to)
}
@ -285,8 +399,10 @@ private[cluster] final class ClusterDomainEventPublisher extends Actor with Acto
latestGossip = newGossip
// first publish the diffUnreachable between the last two gossips
diffUnreachable(oldGossip, newGossip) foreach publish
val newMemberEvents = diffMemberEvents(oldGossip, newGossip)
convertToInstantMemberEvents(newMemberEvents) foreach publish
// buffer up the MemberEvents waiting for convergence
memberEvents ++= diffMemberEvents(oldGossip, newGossip)
memberEvents ++= newMemberEvents
// if we have convergence then publish the MemberEvents and possibly a LeaderChanged
if (newGossip.convergence) {
val previousConvergedGossip = latestConvergedGossip