2012-02-20 17:22:07 +01:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
|
|
|
*/
|
2012-02-28 17:04:48 +01:00
|
|
|
|
2012-02-20 17:22:07 +01:00
|
|
|
package akka.cluster
|
|
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
import com.typesafe.config.ConfigFactory
|
|
|
|
|
import akka.remote.testkit.MultiNodeConfig
|
|
|
|
|
import akka.remote.testkit.MultiNodeSpec
|
2012-02-20 17:22:07 +01:00
|
|
|
import akka.testkit._
|
|
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
object LeaderElectionMultiJvmSpec extends MultiNodeConfig {
|
|
|
|
|
val first = role("first")
|
|
|
|
|
val second = role("second")
|
|
|
|
|
val third = role("third")
|
2012-05-28 11:05:02 +02:00
|
|
|
val fourth = role("fourth")
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
commonConfig(debugConfig(on = false).
|
|
|
|
|
withFallback(ConfigFactory.parseString("""
|
|
|
|
|
akka.cluster.auto-down = off
|
|
|
|
|
""")).
|
|
|
|
|
withFallback(MultiNodeClusterSpec.clusterConfig))
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
}
|
2012-03-09 12:56:56 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
class LeaderElectionMultiJvmNode1 extends LeaderElectionSpec
|
|
|
|
|
class LeaderElectionMultiJvmNode2 extends LeaderElectionSpec
|
|
|
|
|
class LeaderElectionMultiJvmNode3 extends LeaderElectionSpec
|
|
|
|
|
class LeaderElectionMultiJvmNode4 extends LeaderElectionSpec
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
abstract class LeaderElectionSpec extends MultiNodeSpec(LeaderElectionMultiJvmSpec) with MultiNodeClusterSpec {
|
|
|
|
|
import LeaderElectionMultiJvmSpec._
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
override def initialParticipants = 4
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-27 19:15:31 +02:00
|
|
|
lazy val firstAddress = node(first).address
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
// sorted in the order used by the cluster
|
2012-05-28 11:05:02 +02:00
|
|
|
lazy val roles = Seq(first, second, third, fourth).sorted
|
2012-03-09 12:56:56 +01:00
|
|
|
|
2012-05-27 19:15:31 +02:00
|
|
|
"A cluster of four nodes" must {
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-27 19:15:31 +02:00
|
|
|
"be able to 'elect' a single leader" taggedAs LongRunningTest in {
|
2012-05-28 11:05:02 +02:00
|
|
|
// make sure that the node-to-join is started before other join
|
2012-05-25 14:48:00 +02:00
|
|
|
runOn(first) {
|
|
|
|
|
cluster
|
2012-02-20 17:22:07 +01:00
|
|
|
}
|
2012-05-25 14:48:00 +02:00
|
|
|
testConductor.enter("first-started")
|
|
|
|
|
|
|
|
|
|
cluster.join(firstAddress)
|
2012-05-28 11:05:02 +02:00
|
|
|
awaitUpConvergence(numberOfMembers = roles.size)
|
2012-05-25 14:48:00 +02:00
|
|
|
cluster.isLeader must be(mySelf == roles.head)
|
|
|
|
|
testConductor.enter("after")
|
2012-02-20 17:22:07 +01:00
|
|
|
}
|
|
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
def shutdownLeaderAndVerifyNewLeader(alreadyShutdown: Int): Unit = {
|
|
|
|
|
val currentRoles = roles.drop(alreadyShutdown)
|
|
|
|
|
currentRoles.size must be >= (2)
|
|
|
|
|
|
|
|
|
|
runOn(currentRoles.head) {
|
|
|
|
|
cluster.shutdown()
|
|
|
|
|
testConductor.enter("after-shutdown")
|
|
|
|
|
testConductor.enter("after-down")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// runOn previously shutdown cluster nodes
|
|
|
|
|
if ((roles diff currentRoles).contains(mySelf)) {
|
|
|
|
|
testConductor.enter("after-shutdown")
|
|
|
|
|
testConductor.enter("after-down")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// runOn remaining cluster nodes
|
|
|
|
|
if (currentRoles.tail.contains(mySelf)) {
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
testConductor.enter("after-shutdown")
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
runOn(currentRoles.last) {
|
|
|
|
|
// user marks the shutdown leader as DOWN
|
|
|
|
|
val leaderAddress = node(currentRoles.head).address
|
|
|
|
|
cluster.down(leaderAddress)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testConductor.enter("after-down")
|
|
|
|
|
|
|
|
|
|
awaitUpConvergence(currentRoles.size - 1)
|
|
|
|
|
val nextExpectedLeader = currentRoles.tail.head
|
|
|
|
|
cluster.isLeader must be(mySelf == nextExpectedLeader)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testConductor.enter("after")
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-27 19:15:31 +02:00
|
|
|
"be able to 're-elect' a single leader after leader has left" taggedAs LongRunningTest in {
|
2012-05-25 14:48:00 +02:00
|
|
|
shutdownLeaderAndVerifyNewLeader(alreadyShutdown = 0)
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-27 19:15:31 +02:00
|
|
|
"be able to 're-elect' a single leader after leader has left (again)" taggedAs LongRunningTest in {
|
2012-05-25 14:48:00 +02:00
|
|
|
shutdownLeaderAndVerifyNewLeader(alreadyShutdown = 1)
|
|
|
|
|
}
|
2012-02-20 17:22:07 +01:00
|
|
|
}
|
2012-05-25 14:48:00 +02:00
|
|
|
|
2012-02-20 17:22:07 +01:00
|
|
|
}
|