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 {
|
2012-05-28 13:55:22 +02:00
|
|
|
val controller = role("controller")
|
2012-05-25 14:48:00 +02:00
|
|
|
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-28 15:38:39 +02:00
|
|
|
commonConfig(debugConfig(on = false).withFallback(MultiNodeClusterSpec.clusterConfig))
|
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-05-28 13:55:22 +02:00
|
|
|
class LeaderElectionMultiJvmNode5 extends LeaderElectionSpec
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-06-04 23:21:28 +02:00
|
|
|
abstract class LeaderElectionSpec
|
|
|
|
|
extends MultiNodeSpec(LeaderElectionMultiJvmSpec)
|
|
|
|
|
with MultiNodeClusterSpec {
|
|
|
|
|
|
2012-05-25 14:48:00 +02:00
|
|
|
import LeaderElectionMultiJvmSpec._
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-28 13:55:22 +02:00
|
|
|
override def initialParticipants = 5
|
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) {
|
2012-06-04 23:21:28 +02:00
|
|
|
startClusterNode()
|
2012-02-20 17:22:07 +01:00
|
|
|
}
|
2012-05-25 14:48:00 +02:00
|
|
|
testConductor.enter("first-started")
|
|
|
|
|
|
2012-05-28 15:29:00 +02:00
|
|
|
if (myself != controller) {
|
2012-05-28 13:55:22 +02:00
|
|
|
cluster.join(firstAddress)
|
|
|
|
|
awaitUpConvergence(numberOfMembers = roles.size)
|
2012-05-28 15:29:00 +02:00
|
|
|
cluster.isLeader must be(myself == roles.head)
|
2012-06-01 11:37:44 +02:00
|
|
|
assertLeaderIn(roles)
|
2012-05-28 13:55:22 +02:00
|
|
|
}
|
2012-05-25 14:48:00 +02:00
|
|
|
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)
|
2012-05-28 13:55:22 +02:00
|
|
|
val leader = currentRoles.head
|
|
|
|
|
val aUser = currentRoles.last
|
2012-06-01 11:37:44 +02:00
|
|
|
val remainingRoles = currentRoles.tail
|
2012-05-25 14:48:00 +02:00
|
|
|
|
2012-05-28 15:29:00 +02:00
|
|
|
myself match {
|
2012-05-25 14:48:00 +02:00
|
|
|
|
2012-05-28 13:55:22 +02:00
|
|
|
case `controller` ⇒
|
|
|
|
|
testConductor.enter("before-shutdown")
|
|
|
|
|
testConductor.shutdown(leader, 0)
|
|
|
|
|
testConductor.enter("after-shutdown", "after-down", "completed")
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-28 13:55:22 +02:00
|
|
|
case `leader` ⇒
|
|
|
|
|
testConductor.enter("before-shutdown")
|
|
|
|
|
// this node will be shutdown by the controller and doesn't participate in more barriers
|
2012-02-20 17:22:07 +01:00
|
|
|
|
2012-05-28 13:55:22 +02:00
|
|
|
case `aUser` ⇒
|
|
|
|
|
val leaderAddress = node(leader).address
|
|
|
|
|
testConductor.enter("before-shutdown", "after-shutdown")
|
2012-05-25 14:48:00 +02:00
|
|
|
// user marks the shutdown leader as DOWN
|
|
|
|
|
cluster.down(leaderAddress)
|
2012-05-28 13:55:22 +02:00
|
|
|
testConductor.enter("after-down", "completed")
|
2012-05-25 14:48:00 +02:00
|
|
|
|
2012-06-01 11:37:44 +02:00
|
|
|
case _ if remainingRoles.contains(myself) ⇒
|
2012-05-28 13:55:22 +02:00
|
|
|
// remaining cluster nodes, not shutdown
|
|
|
|
|
testConductor.enter("before-shutdown", "after-shutdown", "after-down")
|
|
|
|
|
|
|
|
|
|
awaitUpConvergence(currentRoles.size - 1)
|
2012-06-01 11:37:44 +02:00
|
|
|
val nextExpectedLeader = remainingRoles.head
|
2012-05-28 15:29:00 +02:00
|
|
|
cluster.isLeader must be(myself == nextExpectedLeader)
|
2012-06-01 11:37:44 +02:00
|
|
|
assertLeaderIn(remainingRoles)
|
2012-05-28 13:55:22 +02:00
|
|
|
|
|
|
|
|
testConductor.enter("completed")
|
2012-05-25 14:48:00 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|