Configure exit code and Coordinated Shutdown per-reason overrides (#25186)

* Enable exit code configurability

* Rollback first approach. Implement override

* Support overriding exit-jvm and terminate-actor-sys

* Simplifies code handling overrides
This commit is contained in:
Ignasi Marimon-Clos 2018-06-06 09:45:55 +02:00 committed by Christopher Batey
parent cab48ba699
commit 9f6fc2d6da
3 changed files with 71 additions and 30 deletions

View file

@ -323,6 +323,18 @@ class CoordinatedShutdownSpec extends AkkaSpec(ConfigFactory.parseString(
"c" Phase(dependsOn = Set("a", "b"), timeout = 10.seconds, recover = false, enabled = true))) "c" Phase(dependsOn = Set("a", "b"), timeout = 10.seconds, recover = false, enabled = true)))
} }
"default exit code to 0" in {
lazy val conf = ConfigFactory.load().getConfig("akka.coordinated-shutdown")
val confWithOverrides = CoordinatedShutdown.confWithOverrides(conf, None)
confWithOverrides.getInt("exit-code") should ===(0)
}
"default exit code to -1 when the Reason is ClusterDowning" in {
lazy val conf = ConfigFactory.load().getConfig("akka.coordinated-shutdown")
val confWithOverrides = CoordinatedShutdown.confWithOverrides(conf, Some(CoordinatedShutdown.ClusterDowningReason))
confWithOverrides.getInt("exit-code") should ===(-1)
}
// this must be the last test, since it terminates the ActorSystem // this must be the last test, since it terminates the ActorSystem
"terminate ActorSystem" in { "terminate ActorSystem" in {
Await.result(CoordinatedShutdown(system).run(CustomReason), 10.seconds) should ===(Done) Await.result(CoordinatedShutdown(system).run(CustomReason), 10.seconds) should ===(Done)

View file

@ -1056,11 +1056,28 @@ akka {
# immediately when the last phase is reached. # immediately when the last phase is reached.
exit-jvm = off exit-jvm = off
# Exit status to use on System.exit(int) when 'exit-jvm' is 'on'.
exit-code = 0
# Run the coordinated shutdown when the JVM process exits, e.g. # Run the coordinated shutdown when the JVM process exits, e.g.
# via kill SIGTERM signal (SIGINT ctrl-c doesn't work). # via kill SIGTERM signal (SIGINT ctrl-c doesn't work).
# This property is related to `akka.jvm-shutdown-hooks` above. # This property is related to `akka.jvm-shutdown-hooks` above.
run-by-jvm-shutdown-hook = on run-by-jvm-shutdown-hook = on
# When Coordinated Shutdown is triggered an instance of `Reason` is
# required. That value can be used to override the default settings.
# Only 'exit-jvm', 'exit-code' and 'terminate-actor-system' may be
# overriden depending on the reason.
reason-overrides {
# Overrides are applied using the `reason.getClass.getName`. This
# default overrides the `exit-code` when the `Reason` is a cluster
# Downing event (identified by the object
# "akka.actor.CoordinatedShutdown$ClusterDowningReason$").
"akka.actor.CoordinatedShutdown$ClusterDowningReason$" {
exit-code = -1
}
}
#//#coordinated-shutdown-phases #//#coordinated-shutdown-phases
# CoordinatedShutdown is enabled by default and will run the tasks that # CoordinatedShutdown is enabled by default and will run the tasks that
# are added to these phases by individual Akka modules and user logic. # are added to these phases by individual Akka modules and user logic.

View file

@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference
import java.util.function.Supplier import java.util.function.Supplier
import java.util.Optional import java.util.Optional
import akka.annotation.InternalApi
import akka.util.OptionVal import akka.util.OptionVal
object CoordinatedShutdown extends ExtensionId[CoordinatedShutdown] with ExtensionIdProvider { object CoordinatedShutdown extends ExtensionId[CoordinatedShutdown] with ExtensionIdProvider {
@ -171,38 +172,49 @@ object CoordinatedShutdown extends ExtensionId[CoordinatedShutdown] with Extensi
coord coord
} }
private def initPhaseActorSystemTerminate(system: ActorSystem, conf: Config, coord: CoordinatedShutdown): Unit = { // locate reason-specific overrides and merge with defaults.
val terminateActorSystem = conf.getBoolean("terminate-actor-system") @InternalApi private[akka] def confWithOverrides(conf: Config, reason: Option[Reason]): Config = {
val exitJvm = conf.getBoolean("exit-jvm") reason.flatMap { r
if (terminateActorSystem || exitJvm) { val basePath = s"""reason-overrides."${r.getClass.getName}""""
coord.addTask(PhaseActorSystemTerminate, "terminate-system") { () if (conf.hasPath(basePath)) Some(conf.getConfig(basePath).withFallback(conf)) else None
if (exitJvm && terminateActorSystem) { }.getOrElse(
// In case ActorSystem shutdown takes longer than the phase timeout, conf
// exit the JVM forcefully anyway. )
// We must spawn a separate thread to not block current thread, }
// since that would have blocked the shutdown of the ActorSystem.
val timeout = coord.timeout(PhaseActorSystemTerminate)
val t = new Thread {
override def run(): Unit = {
if (Try(Await.ready(system.whenTerminated, timeout)).isFailure && !runningJvmHook)
System.exit(0)
}
}
t.setName("CoordinatedShutdown-exit")
t.start()
}
if (terminateActorSystem) { private def initPhaseActorSystemTerminate(system: ActorSystem, conf: Config, coord: CoordinatedShutdown): Unit = {
system.terminate().map { _ coord.addTask(PhaseActorSystemTerminate, "terminate-system") { ()
if (exitJvm && !runningJvmHook) System.exit(0) val confForReason = confWithOverrides(conf, coord.shutdownReason())
Done val terminateActorSystem = confForReason.getBoolean("terminate-actor-system")
}(ExecutionContexts.sameThreadExecutionContext) val exitJvm = confForReason.getBoolean("exit-jvm")
} else if (exitJvm) { val exitCode = confForReason.getInt("exit-code")
System.exit(0)
Future.successful(Done) if (exitJvm && terminateActorSystem) {
} else // In case ActorSystem shutdown takes longer than the phase timeout,
Future.successful(Done) // exit the JVM forcefully anyway.
// We must spawn a separate thread to not block current thread,
// since that would have blocked the shutdown of the ActorSystem.
val timeout = coord.timeout(PhaseActorSystemTerminate)
val t = new Thread {
override def run(): Unit = {
if (Try(Await.ready(system.whenTerminated, timeout)).isFailure && !runningJvmHook)
System.exit(exitCode)
}
}
t.setName("CoordinatedShutdown-exit")
t.start()
} }
if (terminateActorSystem) {
system.terminate().map { _
if (exitJvm && !runningJvmHook) System.exit(exitCode)
Done
}(ExecutionContexts.sameThreadExecutionContext)
} else if (exitJvm) {
System.exit(exitCode)
Future.successful(Done)
} else
Future.successful(Done)
} }
} }