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:
parent
4a9c753710
commit
84ade6fdc3
69 changed files with 1778 additions and 339 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue