/** * Copyright (C) 2009-2014 Typesafe Inc. */ package akka.cluster import scala.collection.immutable import scala.language.postfixOps import scala.concurrent.duration._ import akka.actor.Address import akka.cluster.MemberStatus._ import akka.remote.testkit.MultiNodeConfig import akka.remote.testkit.MultiNodeSpec import akka.testkit._ import com.typesafe.config.ConfigFactory import org.scalatest.BeforeAndAfter import akka.actor.ActorSystem import akka.actor.ActorRef import akka.event.Logging.Info import akka.actor.Actor import akka.actor.Props object NodeChurnMultiJvmSpec extends MultiNodeConfig { val first = role("first") val second = role("second") val third = role("third") commonConfig(debugConfig(on = false). withFallback(ConfigFactory.parseString(""" akka.cluster.auto-down-unreachable-after = 1s akka.remote.log-frame-size-exceeding = 2000b """)). withFallback(MultiNodeClusterSpec.clusterConfig)) class LogListener(testActor: ActorRef) extends Actor { def receive = { case Info(_, _, msg: String) if msg.startsWith("New maximum payload size for [akka.cluster.GossipEnvelope]") ⇒ testActor ! msg case _ ⇒ } } } class NodeChurnMultiJvmNode1 extends NodeChurnSpec class NodeChurnMultiJvmNode2 extends NodeChurnSpec class NodeChurnMultiJvmNode3 extends NodeChurnSpec abstract class NodeChurnSpec extends MultiNodeSpec(NodeChurnMultiJvmSpec) with MultiNodeClusterSpec with ImplicitSender { import NodeChurnMultiJvmSpec._ def seedNodes: immutable.IndexedSeq[Address] = Vector(first, second, third) override def afterAll(): Unit = { super.afterAll() } val rounds = 3 override def expectedTestDuration: FiniteDuration = 45.seconds * rounds def awaitAllMembersUp(additionaSystems: Vector[ActorSystem]): Unit = { val numberOfMembers = roles.size + roles.size * additionaSystems.size awaitMembersUp(numberOfMembers) awaitAssert { additionaSystems.foreach { s ⇒ val c = Cluster(s) c.state.members.size should be(numberOfMembers) c.state.members.forall(_.status == MemberStatus.Up) } } } def awaitRemoved(additionaSystems: Vector[ActorSystem]): Unit = { awaitMembersUp(roles.size, timeout = 40.seconds) awaitAssert { additionaSystems.foreach { s ⇒ Cluster(s).isTerminated should be(true) } } } "Cluster with short lived members" must { "setup stable nodes" taggedAs LongRunningTest in within(15.seconds) { val logListener = system.actorOf(Props(classOf[LogListener], testActor), "logListener") system.eventStream.subscribe(logListener, classOf[Info]) cluster.joinSeedNodes(seedNodes) awaitMembersUp(roles.size) } "join and remove transient nodes without growing gossip payload" taggedAs LongRunningTest in { // This test is configured with log-frame-size-exceeding and the LogListener // will send to the testActor if unexpected increase in message payload size. // It will fail after a while if vector clock entries of removed nodes are not pruned. for (n ← 1 to rounds) { log.info("round-" + n) val systems = Vector.fill(5)(ActorSystem(system.name, system.settings.config)) systems.foreach { s ⇒ muteDeadLetters()(s) Cluster(s).joinSeedNodes(seedNodes) } awaitAllMembersUp(systems) enterBarrier("members-up-" + n) systems.foreach { node ⇒ if (n % 2 == 0) Cluster(node).down(Cluster(node).selfAddress) else Cluster(node).leave(Cluster(node).selfAddress) } awaitRemoved(systems) enterBarrier("members-removed-" + n) systems.foreach(_.terminate().await) log.info("end of round-" + n) // log listener will send to testActor if payload size exceed configured log-frame-size-exceeding expectNoMsg(2.seconds) } expectNoMsg(5.seconds) } } }