From 3010dc207a3c32cec392141c205bec176fdc0c6a Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 20 Sep 2022 05:19:37 +0900 Subject: [PATCH] refactor(backend): refactor ChartManagementService --- .../src/core/chart/ChartManagementService.ts | 30 +++--- packages/backend/src/misc/before-shutdown.ts | 94 ------------------- 2 files changed, 19 insertions(+), 105 deletions(-) delete mode 100644 packages/backend/src/misc/before-shutdown.ts diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 2020ca0ca6..6476cd6843 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -1,5 +1,4 @@ import { Injectable, Inject } from '@nestjs/common'; -import { beforeShutdown } from '@/misc/before-shutdown.js'; import FederationChart from './charts/federation.js'; import NotesChart from './charts/notes.js'; @@ -13,9 +12,13 @@ import HashtagChart from './charts/hashtag.js'; import PerUserFollowingChart from './charts/per-user-following.js'; import PerUserDriveChart from './charts/per-user-drive.js'; import ApRequestChart from './charts/ap-request.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() -export class ChartManagementService { +export class ChartManagementService implements OnApplicationShutdown { + private charts; + private saveIntervalId: NodeJS.Timer; + constructor( private federationChart: FederationChart, private notesChart: NotesChart, @@ -29,10 +32,8 @@ export class ChartManagementService { private perUserFollowingChart: PerUserFollowingChart, private perUserDriveChart: PerUserDriveChart, private apRequestChart: ApRequestChart, - ) {} - - public async run() { - const charts = [ + ) { + this.charts = [ this.federationChart, this.notesChart, this.usersChart, @@ -46,14 +47,21 @@ export class ChartManagementService { this.perUserDriveChart, this.apRequestChart, ]; - + } + + public async run() { // 20分おきにメモリ情報をDBに書き込み - setInterval(() => { - for (const chart of charts) { + this.saveIntervalId = setInterval(() => { + for (const chart of this.charts) { chart.save(); } }, 1000 * 60 * 20); - - beforeShutdown(() => Promise.all(charts.map(chart => chart.save()))); + } + + async onApplicationShutdown(signal: string): Promise { + clearInterval(this.saveIntervalId); + await Promise.all( + this.charts.map(chart => chart.save()), + ); } } diff --git a/packages/backend/src/misc/before-shutdown.ts b/packages/backend/src/misc/before-shutdown.ts deleted file mode 100644 index 74489293d7..0000000000 --- a/packages/backend/src/misc/before-shutdown.ts +++ /dev/null @@ -1,94 +0,0 @@ -// https://gist.github.com/nfantone/1eaa803772025df69d07f4dbf5df7e58 - -'use strict'; - -/** - * @callback BeforeShutdownListener - * @param {string} [signalOrEvent] The exit signal or event name received on the process. - */ - -/** - * System signals the app will listen to initiate shutdown. - * @const {string[]} - */ -const SHUTDOWN_SIGNALS = ['SIGINT', 'SIGTERM']; - -/** - * Time in milliseconds to wait before forcing shutdown. - * @const {number} - */ -const SHUTDOWN_TIMEOUT = 15000; - -/** - * A queue of listener callbacks to execute before shutting - * down the process. - * @type {BeforeShutdownListener[]} - */ -const shutdownListeners: ((signalOrEvent: string) => void)[] = []; - -/** - * Listen for signals and execute given `fn` function once. - * @param {string[]} signals System signals to listen to. - * @param {function(string)} fn Function to execute on shutdown. - */ -const processOnce = (signals: string[], fn: (signalOrEvent: string) => void) => { - for (const sig of signals) { - process.once(sig, fn); - } -}; - -/** - * Sets a forced shutdown mechanism that will exit the process after `timeout` milliseconds. - * @param {number} timeout Time to wait before forcing shutdown (milliseconds) - */ -const forceExitAfter = (timeout: number) => () => { - setTimeout(() => { - // Force shutdown after timeout - console.warn(`Could not close resources gracefully after ${timeout}ms: forcing shutdown`); - return process.exit(1); - }, timeout).unref(); -}; - -/** - * Main process shutdown handler. Will invoke every previously registered async shutdown listener - * in the queue and exit with a code of `0`. Any `Promise` rejections from any listener will - * be logged out as a warning, but won't prevent other callbacks from executing. - * @param {string} signalOrEvent The exit signal or event name received on the process. - */ -async function shutdownHandler(signalOrEvent: string) { - if (process.env.NODE_ENV === 'test') return process.exit(0); - - console.warn(`Shutting down: received [${signalOrEvent}] signal`); - - for (const listener of shutdownListeners) { - try { - await listener(signalOrEvent); - } catch (err) { - if (err instanceof Error) { - console.warn(`A shutdown handler failed before completing with: ${err.message ?? err}`); - } - } - } - - return process.exit(0); -} - -/** - * Registers a new shutdown listener to be invoked before exiting - * the main process. Listener handlers are guaranteed to be called in the order - * they were registered. - * @param {BeforeShutdownListener} listener The shutdown listener to register. - * @returns {BeforeShutdownListener} Echoes back the supplied `listener`. - */ -export function beforeShutdown(listener: () => void) { - shutdownListeners.push(listener); - return listener; -} - -// Register shutdown callback that kills the process after `SHUTDOWN_TIMEOUT` milliseconds -// This prevents custom shutdown handlers from hanging the process indefinitely -processOnce(SHUTDOWN_SIGNALS, forceExitAfter(SHUTDOWN_TIMEOUT)); - -// Register process shutdown callback -// Will listen to incoming signal events and execute all registered handlers in the stack -processOnce(SHUTDOWN_SIGNALS, shutdownHandler);