From a10312754515cf7f53cd002ad4ecd064e2795abc Mon Sep 17 00:00:00 2001 From: LK HO Date: Mon, 12 Apr 2021 23:45:33 +0800 Subject: [PATCH] fix: graceful shutdown (#3821) * fix: missing graceful shutdown handler * fix: asar - use async fs operation * fix: scheduler - graceful shutdown and wait for running jobs to complete --- server/core/asar.js | 8 ++++---- server/core/kernel.js | 18 ++++++++++-------- server/core/scheduler.js | 23 ++++++++++++----------- server/index.js | 13 +++++++++++++ 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/server/core/asar.js b/server/core/asar.js index 6c76468d..25099016 100644 --- a/server/core/asar.js +++ b/server/core/asar.js @@ -40,11 +40,11 @@ module.exports = { } }, async unload () { - if (this.fdCache) { + const fds = Object.values(this.fdCache) + if (fds.length > 0) { WIKI.logger.info('Closing ASAR file descriptors...') - for (const fdItem in this.fdCache) { - fs.closeSync(this.fdCache[fdItem].fd) - } + const closeAsync = require('util').promisify(fs.close) + await Promise.all(fds.map(x => closeAsync(x.fd))) this.fdCache = {} } }, diff --git a/server/core/kernel.js b/server/core/kernel.js index af22c902..6dcb17cf 100644 --- a/server/core/kernel.js +++ b/server/core/kernel.js @@ -107,19 +107,21 @@ module.exports = { * Graceful shutdown */ async shutdown () { - if (WIKI.models) { - await WIKI.models.unsubscribeToNotifications() - await WIKI.models.knex.client.pool.destroy() - await WIKI.models.knex.destroy() + if (WIKI.servers) { + await WIKI.servers.stopServers() } if (WIKI.scheduler) { - WIKI.scheduler.stop() + await WIKI.scheduler.stop() + } + if (WIKI.models) { + await WIKI.models.unsubscribeToNotifications() + if (WIKI.models.knex) { + await WIKI.models.knex.destroy() + } } if (WIKI.asar) { await WIKI.asar.unload() } - if (WIKI.servers) { - await WIKI.servers.stopServers() - } + process.exit(0) } } diff --git a/server/core/scheduler.js b/server/core/scheduler.js index 7216427e..67fdde58 100644 --- a/server/core/scheduler.js +++ b/server/core/scheduler.js @@ -12,7 +12,8 @@ class Job { schedule = 'P1D', repeat = false, worker = false - }) { + }, queue) { + this.queue = queue this.finished = Promise.resolve() this.name = name this.immediate = immediate @@ -27,6 +28,7 @@ class Job { * @param {Object} data Job Data */ start(data) { + this.queue.jobs.push(this) if (this.immediate) { this.invoke(data) } else { @@ -82,16 +84,20 @@ class Job { } catch (err) { WIKI.logger.warn(err) } - if (this.repeat) { + if (this.repeat && this.queue.jobs.includes(this)) { this.queue(data) + } else { + this.stop().catch(() => {}) } } /** * Stop any future job invocation from occuring */ - stop() { + async stop() { clearTimeout(this.timeout) + this.queue.jobs = this.queue.jobs.filter(x => x !== this) + return this.finished } } @@ -118,16 +124,11 @@ module.exports = { }) }, registerJob(opts, data) { - const job = new Job(opts) + const job = new Job(opts, this) job.start(data) - if (job.repeat) { - this.jobs.push(job) - } return job }, - stop() { - this.jobs.forEach(job => { - job.stop() - }) + async stop() { + return Promise.all(this.jobs.map(job => job.stop())) } } diff --git a/server/index.js b/server/index.js index 254335c9..87eff16b 100644 --- a/server/index.js +++ b/server/index.js @@ -33,3 +33,16 @@ WIKI.logger = require('./core/logger').init('MASTER') // ---------------------------------------- WIKI.kernel.init() + +// ---------------------------------------- +// Register exit handler +// ---------------------------------------- + +process.on('SIGINT', () => { + WIKI.kernel.shutdown() +}) +process.on('message', (msg) => { + if (msg === 'shutdown') { + WIKI.kernel.shutdown() + } +})