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:
parent
cab48ba699
commit
9f6fc2d6da
3 changed files with 71 additions and 30 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue