1
0
Fork 1
mirror of https://gitlab.com/mangadex-pub/mangadex_at_home.git synced 2024-01-19 02:48:37 +00:00

Fix graceful shutdown

This commit is contained in:
carbotaniuman 2021-02-11 09:11:03 -06:00
parent e246924b57
commit d1d7fcca7f
5 changed files with 41 additions and 31 deletions

View file

@ -23,7 +23,7 @@
# The size in mebibytes of the cache # The size in mebibytes of the cache
# You can use megabytes instead in a pinch, # You can use megabytes instead in a pinch,
# but just know the two are **NOT** the same. # but just know the two are **NOT** the same.
max_cache_size_in_mebibytes: 1024 max_cache_size_in_mebibytes: 0
# Optional settings for fancy geoip analytics # Optional settings for fancy geoip analytics
metrics_settings: metrics_settings:

View file

@ -32,7 +32,11 @@ object Main {
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
CommandLine(ClientArgs()).execute(*args) try {
CommandLine(ClientArgs()).execute(*args)
} catch (e: Throwable) {
LOGGER.error(e) { "Critical Error " }
}
} }
fun dieWithError(e: Throwable): Nothing { fun dieWithError(e: Throwable): Nothing {

View file

@ -175,8 +175,14 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
storage.maxSize = (newSettings.maxCacheSizeInMebibytes * 1024 * 1024 * 0.95).toLong() storage.maxSize = (newSettings.maxCacheSizeInMebibytes * 1024 * 1024 * 0.95).toLong()
stopImageServer() if (imageServer != null) {
startImageServer() stopImageServer()
}
try {
startImageServer()
} catch (e: Exception) {
LOGGER.warn(e) { "Error starting the image server" }
}
} catch (e: UnrecognizedPropertyException) { } catch (e: UnrecognizedPropertyException) {
LOGGER.warn { "Settings file is invalid: '$e.propertyName' is not a valid setting" } LOGGER.warn { "Settings file is invalid: '$e.propertyName' is not a valid setting" }
} catch (e: JsonProcessingException) { } catch (e: JsonProcessingException) {
@ -189,8 +195,8 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
} }
private fun validateSettings(settings: ClientSettings) { private fun validateSettings(settings: ClientSettings) {
if (settings.maxCacheSizeInMebibytes < 1024) { if (settings.maxCacheSizeInMebibytes < 20480) {
throw ClientSettingsException("Config Error: Invalid max cache size, must be >= 1024 MiB (1GiB)") throw ClientSettingsException("Config Error: Invalid max cache size, must be >= 20480 MiB (20 GiB)")
} }
fun isSecretValid(clientSecret: String): Boolean { fun isSecretValid(clientSecret: String): Boolean {

View file

@ -58,7 +58,7 @@ object Shutdown : State()
data class GracefulStop( data class GracefulStop(
val lastRunning: Running, val lastRunning: Running,
val counts: Int = 0, val counts: Int = 0,
val nextState: State = Uninitialized, val nextState: State,
val action: () -> Unit = {} val action: () -> Unit = {}
) : State() ) : State()
@ -193,7 +193,7 @@ class ServerManager(
if (settings.serverSettings.maxMebibytesPerHour != 0L && settings.serverSettings.maxMebibytesPerHour * 1024 * 1024 /* MiB to bytes */ < currentBytesSent) { if (settings.serverSettings.maxMebibytesPerHour != 0L && settings.serverSettings.maxMebibytesPerHour * 1024 * 1024 /* MiB to bytes */ < currentBytesSent) {
LOGGER.info { "Stopping image server as hourly bandwidth limit reached" } LOGGER.info { "Stopping image server as hourly bandwidth limit reached" }
this.state = GracefulStop(lastRunning = state) this.state = GracefulStop(lastRunning = state, nextState = Uninitialized)
} else { } else {
pingControl() pingControl()
} }
@ -245,7 +245,7 @@ class ServerManager(
if (!state.settings.logicalEqual(newSettings)) { if (!state.settings.logicalEqual(newSettings)) {
LOGGER.info { "Doing internal restart of HTTP server to refresh settings" } LOGGER.info { "Doing internal restart of HTTP server to refresh settings" }
this.state = GracefulStop(lastRunning = state) { this.state = GracefulStop(lastRunning = state, nextState = Uninitialized) {
loginAndStartServer() loginAndStartServer()
} }
} }

View file

@ -77,9 +77,9 @@ sealed class NettyTransport(threads: Int) {
) )
fun shutdownGracefully() { fun shutdownGracefully() {
bossGroup.shutdownGracefully() bossGroup.shutdownGracefully().sync()
workerGroup.shutdownGracefully() workerGroup.shutdownGracefully().sync()
executor.shutdownGracefully() executor.shutdownGracefully().sync()
} }
private class NioTransport(threads: Int) : NettyTransport(threads) { private class NioTransport(threads: Int) : NettyTransport(threads) {
@ -111,21 +111,25 @@ sealed class NettyTransport(threads: Int) {
LOGGER.info { "Choosing a transport using $threadsToUse threads" } LOGGER.info { "Choosing a transport using $threadsToUse threads" }
if (name.startsWith("linux")) { if (name.startsWith("linux")) {
if (IOUring.isAvailable()) { if (!SystemPropertyUtil.get("no-iouring").toBoolean()) {
LOGGER.info { "Using IOUring transport" } if (IOUring.isAvailable()) {
return IOUringTransport(threadsToUse) LOGGER.info { "Using IOUring transport" }
} else { return IOUringTransport(threadsToUse)
LOGGER.info(IOUring.unavailabilityCause()) { } else {
"IOUring transport not available" LOGGER.info(IOUring.unavailabilityCause()) {
"IOUring transport not available"
}
} }
} }
if (Epoll.isAvailable()) { if (!SystemPropertyUtil.get("no-epoll").toBoolean()) {
LOGGER.info { "Using Epoll transport" } if (Epoll.isAvailable()) {
return EpollTransport(threadsToUse) LOGGER.info { "Using Epoll transport" }
} else { return EpollTransport(threadsToUse)
LOGGER.info(Epoll.unavailabilityCause()) { } else {
"Epoll transport not available" LOGGER.info(Epoll.unavailabilityCause()) {
"Epoll transport not available"
}
} }
} }
} }
@ -144,9 +148,7 @@ class Netty(
override fun toServer(httpHandler: HttpHandler): Http4kServer = object : Http4kServer { override fun toServer(httpHandler: HttpHandler): Http4kServer = object : Http4kServer {
private val transport = NettyTransport.bestForPlatform(serverSettings.threads) private val transport = NettyTransport.bestForPlatform(serverSettings.threads)
private lateinit var closeFuture: ChannelFuture private lateinit var channel: Channel
private lateinit var address: InetSocketAddress
private val burstLimiter = object : GlobalTrafficShapingHandler( private val burstLimiter = object : GlobalTrafficShapingHandler(
transport.workerGroup, serverSettings.maxKilobitsPerSecond * 1000L / 8L, 0, 100 transport.workerGroup, serverSettings.maxKilobitsPerSecond * 1000L / 8L, 0, 100
) { ) {
@ -209,14 +211,12 @@ class Netty(
.option(ChannelOption.SO_BACKLOG, 1000) .option(ChannelOption.SO_BACKLOG, 1000)
.childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.SO_KEEPALIVE, true)
val channel = bootstrap.bind(InetSocketAddress(serverSettings.hostname, serverSettings.port)).sync().channel() channel = bootstrap.bind(InetSocketAddress(serverSettings.hostname, serverSettings.port)).sync().channel()
address = channel.localAddress() as InetSocketAddress
closeFuture = channel.closeFuture()
} }
override fun stop() = apply { override fun stop() = apply {
closeFuture.cancel(false)
transport.shutdownGracefully() transport.shutdownGracefully()
channel.closeFuture().sync()
} }
override fun port(): Int = serverSettings.port override fun port(): Int = serverSettings.port