/* * Copyright (C) 2009-2020 Lightbend Inc. */ package akka.cluster import scala.concurrent.duration._ import com.typesafe.config.ConfigFactory import akka.actor.Address import akka.cluster.ClusterEvent.InitialStateAsEvents import akka.cluster.ClusterEvent.MemberUp import akka.remote.testkit.MultiNodeConfig import akka.remote.testkit.MultiNodeSpec import akka.testkit._ // Similar to MultiDcJoinSpec, but slightly different scenario object MultiDcJoin2MultiJvmSpec extends MultiNodeConfig { val first = role("first") val second = role("second") val third = role("third") val fourth = role("fourth") val fifth = role("fifth") nodeConfig(first, second, third)(ConfigFactory.parseString(""" akka { cluster.multi-data-center.self-data-center = alpha } """)) nodeConfig(fourth, fifth)(ConfigFactory.parseString(""" akka { cluster.multi-data-center.self-data-center = beta } """)) commonConfig(ConfigFactory.parseString(""" akka { actor.provider = cluster loggers = ["akka.testkit.TestEventListener"] loglevel = INFO remote.log-remote-lifecycle-events = off cluster { debug.verbose-heartbeat-logging = off debug.verbose-gossip-logging = off multi-data-center { cross-data-center-connections = 1 } } } """)) } class MultiDcJoin2MultiJvmNode1 extends MultiDcJoin2Spec class MultiDcJoin2MultiJvmNode2 extends MultiDcJoin2Spec class MultiDcJoin2MultiJvmNode3 extends MultiDcJoin2Spec class MultiDcJoin2MultiJvmNode4 extends MultiDcJoin2Spec class MultiDcJoin2MultiJvmNode5 extends MultiDcJoin2Spec abstract class MultiDcJoin2Spec extends MultiNodeSpec(MultiDcJoin2MultiJvmSpec) with MultiNodeClusterSpec { import MultiDcJoin2MultiJvmSpec._ "Joining a multi-dc cluster, scenario 2" must { "make sure oldest is selected correctly" taggedAs LongRunningTest in { val observer = TestProbe("beta-observer") runOn(fourth, fifth) { Cluster(system).subscribe(observer.ref, InitialStateAsEvents, classOf[MemberUp]) } val memberUpFromFifth = TestProbe("fromFifth") runOn(fourth) { system.actorOf(TestActors.forwardActorProps(memberUpFromFifth.ref), "fwFromFifth") } // all alpha nodes awaitClusterUp(first, second, third) runOn(fourth) { Cluster(system).join(first) within(20.seconds) { awaitAssert { Cluster(system).state.members .exists(m => m.address == address(fourth) && m.status == MemberStatus.Up) should ===(true) } } } // at the same time join fifth, which is the difference compared to MultiDcJoinSpec runOn(fifth) { Cluster(system).join(second) within(10.seconds) { awaitAssert { Cluster(system).state.members .exists(m => m.address == address(fifth) && m.status == MemberStatus.Up) should ===(true) } } } enterBarrier("beta-joined") runOn(fifth) { val events = observer .receiveN(5) .map(_.asInstanceOf[MemberUp]) .filter(_.member.dataCenter == "beta") .sortBy(_.member.upNumber) events.head.member.upNumber should ===(1) events(1).member.upNumber should ===(2) // only sending Address since it's serializable events.foreach(evt => system.actorSelection(node(fourth) / "user" / "fwFromFifth") ! evt.member.address) } enterBarrier("fw-from-fifth") runOn(fourth) { val events4 = observer .receiveN(5) .map(_.asInstanceOf[MemberUp]) .filter(_.member.dataCenter == "beta") .sortBy(_.member.upNumber) val upFromFifth = memberUpFromFifth.receiveN(2).map(_.asInstanceOf[Address]) events4.head.member.address should ===(upFromFifth.head) events4.head.member.upNumber should ===(1) events4(1).member.upNumber should ===(2) observer.expectNoMessage(2.seconds) } enterBarrier("done") } } }