pekko/akka-cluster/src/test/scala/akka/cluster/ClusterDeathWatchNotificationSpec.scala

169 lines
5.7 KiB
Scala
Raw Normal View History

/*
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.cluster
import scala.concurrent.duration._
import akka.actor._
import akka.testkit._
import com.typesafe.config.ConfigFactory
import akka.remote.artery.ArteryMultiNodeSpec
import akka.remote.artery.ArterySpecSupport
object ClusterDeathWatchNotificationSpec {
val config = ConfigFactory.parseString(s"""
akka {
loglevel = INFO
actor {
provider = cluster
}
}
akka.remote.classic.netty.tcp.port = 0
akka.remote.artery.canonical.port = 0
""").withFallback(ArterySpecSupport.defaultConfig)
object Sender {
def props(receiver: ActorRef, sendOnStop: Vector[String]): Props =
Props(new Sender(receiver, sendOnStop))
}
class Sender(receiver: ActorRef, sendOnStop: Vector[String]) extends Actor {
override def receive: Receive = {
case msg => sender() ! msg
}
override def postStop(): Unit = {
sendOnStop.foreach(receiver ! _)
}
}
}
class ClusterDeathWatchNotificationSpec
extends ArteryMultiNodeSpec(ClusterDeathWatchNotificationSpec.config)
with ImplicitSender {
import ClusterDeathWatchNotificationSpec.Sender
private def system1: ActorSystem = system
private val system2 = newRemoteSystem(name = Some(system.name))
private val system3 = newRemoteSystem(name = Some(system.name))
private val systems = Vector(system1, system2, system3)
private val messages = (1 to 100).map(_.toString).toVector
private def setupSender(sys: ActorSystem, receiverProbe: TestProbe, name: String): Unit = {
val receiverPath = receiverProbe.ref.path.toStringWithAddress(address(system1))
val otherProbe = TestProbe()(sys)
sys.actorSelection(receiverPath).tell(Identify(None), otherProbe.ref)
val receiver = otherProbe.expectMsgType[ActorIdentity](5.seconds).ref.get
receiver.path.address.hasGlobalScope should ===(true) // should be remote
sys.actorOf(Sender.props(receiver, messages), name)
}
private def identifySender(sys: ActorSystem, name: String): ActorRef = {
system1.actorSelection(rootActorPath(sys) / "user" / name) ! Identify(None)
val sender = expectMsgType[ActorIdentity](5.seconds).ref.get
sender
}
"join cluster" in within(10.seconds) {
systems.foreach { sys =>
Cluster(sys).join(Cluster(system1).selfAddress)
}
awaitAssert {
systems.foreach { sys =>
Cluster(sys).state.members.size should ===(systems.size)
Cluster(sys).state.members.iterator.map(_.status).toSet should ===(Set(MemberStatus.Up))
}
}
}
"receive Terminated after ordinary messages" in {
val receiverProbe = TestProbe()
setupSender(system2, receiverProbe, "sender")
val sender = identifySender(system2, "sender")
receiverProbe.watch(sender)
// make it likely that the watch has been established
sender.tell("echo", receiverProbe.ref)
receiverProbe.expectMsg("echo")
sender ! PoisonPill
receiverProbe.receiveN(messages.size).toVector shouldBe messages
receiverProbe.expectTerminated(sender)
}
"receive Terminated after ordinary messages when system is shutdown" in {
val receiverProbe1 = TestProbe()
setupSender(system2, receiverProbe1, "sender1")
val sender1 = identifySender(system2, "sender1")
val receiverProbe2 = TestProbe()
setupSender(system2, receiverProbe2, "sender2")
val sender2 = identifySender(system2, "sender2")
val receiverProbe3 = TestProbe()
setupSender(system2, receiverProbe3, "sender3")
val sender3 = identifySender(system2, "sender3")
receiverProbe1.watch(sender1)
receiverProbe2.watch(sender2)
receiverProbe3.watch(sender3)
// make it likely that the watch has been established
sender1.tell("echo1", receiverProbe1.ref)
receiverProbe1.expectMsg("echo1")
sender2.tell("echo2", receiverProbe2.ref)
receiverProbe2.expectMsg("echo2")
sender3.tell("echo3", receiverProbe3.ref)
receiverProbe3.expectMsg("echo3")
system2.log.debug("terminating")
system2.terminate()
receiverProbe1.receiveN(messages.size, 5.seconds).toVector shouldBe messages
receiverProbe1.expectTerminated(sender1)
receiverProbe2.receiveN(messages.size).toVector shouldBe messages
receiverProbe2.expectTerminated(sender2)
receiverProbe3.receiveN(messages.size).toVector shouldBe messages
receiverProbe3.expectTerminated(sender3)
}
"receive Terminated after ordinary messages when system is leaving" in {
val receiverProbe1 = TestProbe()
setupSender(system3, receiverProbe1, "sender1")
val sender1 = identifySender(system3, "sender1")
val receiverProbe2 = TestProbe()
setupSender(system3, receiverProbe2, "sender2")
val sender2 = identifySender(system3, "sender2")
val receiverProbe3 = TestProbe()
setupSender(system3, receiverProbe3, "sender3")
val sender3 = identifySender(system3, "sender3")
receiverProbe1.watch(sender1)
receiverProbe2.watch(sender2)
receiverProbe3.watch(sender3)
// make it likely that the watch has been established
sender1.tell("echo1", receiverProbe1.ref)
receiverProbe1.expectMsg("echo1")
sender2.tell("echo2", receiverProbe2.ref)
receiverProbe2.expectMsg("echo2")
sender3.tell("echo3", receiverProbe3.ref)
receiverProbe3.expectMsg("echo3")
system3.log.debug("leaving")
Cluster(system1).leave(Cluster(system3).selfAddress)
receiverProbe1.receiveN(messages.size, 5.seconds).toVector shouldBe messages
receiverProbe1.expectTerminated(sender1)
receiverProbe2.receiveN(messages.size).toVector shouldBe messages
receiverProbe2.expectTerminated(sender2)
receiverProbe3.receiveN(messages.size).toVector shouldBe messages
receiverProbe3.expectTerminated(sender3)
}
}