add CoordinatedShutdown, #21537

* CoordinatedShutdown that can run tasks for configured phases in order (DAG)
* coordinate handover/shutdown of singleton with cluster exiting/shutdown
* phase config obj with depends-on list
* integrate graceful leaving of sharding in coordinated shutdown
* add timeout and recover
* add some missing artery ports to tests
* leave via CoordinatedShutdown.run
* optionally exit-jvm in last phase
* run via jvm shutdown hook
* send ExitingConfirmed to leader before shutdown of Exiting
  to not have to wait for failure detector to mark it as
  unreachable before removing
* the unreachable signal is still kept as a safe guard if
  message is lost or leader dies
* PhaseClusterExiting vs MemberExited in ClusterSingletonManager
* terminate ActorSystem when cluster shutdown (via Down)
* add more predefined and custom phases
* reference documentation
* migration guide
* problem when the leader order was sys2, sys1, sys3,
  then sys3 could not perform it's duties and move Leving sys1 to
  Exiting because it was observing sys1 as unreachable
* exclude Leaving with exitingConfirmed from convergence condidtion
This commit is contained in:
Patrik Nordwall 2016-12-01 18:49:38 +01:00
parent 4a9c753710
commit 84ade6fdc3
69 changed files with 1778 additions and 339 deletions

View file

@ -16,6 +16,10 @@ import akka.testkit.TestProbe
import akka.actor.ActorSystem
import akka.actor.Props
import com.typesafe.config.ConfigFactory
import akka.actor.CoordinatedShutdown
import akka.cluster.ClusterEvent.MemberEvent
import akka.cluster.ClusterEvent._
import scala.concurrent.Await
object ClusterSpec {
val config = """
@ -28,7 +32,7 @@ object ClusterSpec {
akka.actor.provider = "cluster"
akka.remote.log-remote-lifecycle-events = off
akka.remote.netty.tcp.port = 0
#akka.loglevel = DEBUG
akka.remote.artery.canonical.port = 0
"""
final case class GossipTo(address: Address)
@ -109,6 +113,7 @@ class ClusterSpec extends AkkaSpec(ClusterSpec.config) with ImplicitSender {
val sys2 = ActorSystem("ClusterSpec2", ConfigFactory.parseString("""
akka.actor.provider = "cluster"
akka.remote.netty.tcp.port = 0
akka.remote.artery.canonical.port = 0
"""))
try {
val ref = sys2.actorOf(Props.empty)
@ -137,5 +142,77 @@ class ClusterSpec extends AkkaSpec(ClusterSpec.config) with ImplicitSender {
cluster.remotePathOf(testActor).uid should ===(testActor.path.uid)
cluster.remotePathOf(testActor).address should ===(selfAddress)
}
"leave via CoordinatedShutdown.run" in {
val sys2 = ActorSystem("ClusterSpec2", ConfigFactory.parseString("""
akka.actor.provider = "cluster"
akka.remote.netty.tcp.port = 0
akka.remote.artery.canonical.port = 0
"""))
try {
val probe = TestProbe()(sys2)
Cluster(sys2).subscribe(probe.ref, classOf[MemberEvent])
probe.expectMsgType[CurrentClusterState]
Cluster(sys2).join(Cluster(sys2).selfAddress)
probe.expectMsgType[MemberUp]
CoordinatedShutdown(sys2).run()
probe.expectMsgType[MemberLeft]
probe.expectMsgType[MemberExited]
probe.expectMsgType[MemberRemoved]
} finally {
shutdown(sys2)
}
}
"terminate ActorSystem via leave (CoordinatedShutdown)" in {
val sys2 = ActorSystem("ClusterSpec2", ConfigFactory.parseString("""
akka.actor.provider = "cluster"
akka.remote.netty.tcp.port = 0
akka.remote.artery.canonical.port = 0
akka.coordinated-shutdown.terminate-actor-system = on
"""))
try {
val probe = TestProbe()(sys2)
Cluster(sys2).subscribe(probe.ref, classOf[MemberEvent])
probe.expectMsgType[CurrentClusterState]
Cluster(sys2).join(Cluster(sys2).selfAddress)
probe.expectMsgType[MemberUp]
Cluster(sys2).leave(Cluster(sys2).selfAddress)
probe.expectMsgType[MemberLeft]
probe.expectMsgType[MemberExited]
probe.expectMsgType[MemberRemoved]
Await.result(sys2.whenTerminated, 10.seconds)
Cluster(sys2).isTerminated should ===(true)
} finally {
shutdown(sys2)
}
}
"terminate ActorSystem via down (CoordinatedShutdown)" in {
val sys3 = ActorSystem("ClusterSpec3", ConfigFactory.parseString("""
akka.actor.provider = "cluster"
akka.remote.netty.tcp.port = 0
akka.remote.artery.canonical.port = 0
akka.coordinated-shutdown.terminate-actor-system = on
akka.cluster.run-coordinated-shutdown-when-down = on
akka.loglevel=DEBUG
"""))
try {
val probe = TestProbe()(sys3)
Cluster(sys3).subscribe(probe.ref, classOf[MemberEvent])
probe.expectMsgType[CurrentClusterState]
Cluster(sys3).join(Cluster(sys3).selfAddress)
probe.expectMsgType[MemberUp]
Cluster(sys3).down(Cluster(sys3).selfAddress)
probe.expectMsgType[MemberRemoved]
Await.result(sys3.whenTerminated, 10.seconds)
Cluster(sys3).isTerminated should ===(true)
} finally {
shutdown(sys3)
}
}
}
}