refactor(backend): refactor ChartManagementService

This commit is contained in:
syuilo 2022-09-20 05:19:37 +09:00
parent 72253a1029
commit 3010dc207a
2 changed files with 19 additions and 105 deletions

View file

@ -1,5 +1,4 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { beforeShutdown } from '@/misc/before-shutdown.js';
import FederationChart from './charts/federation.js'; import FederationChart from './charts/federation.js';
import NotesChart from './charts/notes.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 PerUserFollowingChart from './charts/per-user-following.js';
import PerUserDriveChart from './charts/per-user-drive.js'; import PerUserDriveChart from './charts/per-user-drive.js';
import ApRequestChart from './charts/ap-request.js'; import ApRequestChart from './charts/ap-request.js';
import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class ChartManagementService { export class ChartManagementService implements OnApplicationShutdown {
private charts;
private saveIntervalId: NodeJS.Timer;
constructor( constructor(
private federationChart: FederationChart, private federationChart: FederationChart,
private notesChart: NotesChart, private notesChart: NotesChart,
@ -29,10 +32,8 @@ export class ChartManagementService {
private perUserFollowingChart: PerUserFollowingChart, private perUserFollowingChart: PerUserFollowingChart,
private perUserDriveChart: PerUserDriveChart, private perUserDriveChart: PerUserDriveChart,
private apRequestChart: ApRequestChart, private apRequestChart: ApRequestChart,
) {} ) {
this.charts = [
public async run() {
const charts = [
this.federationChart, this.federationChart,
this.notesChart, this.notesChart,
this.usersChart, this.usersChart,
@ -46,14 +47,21 @@ export class ChartManagementService {
this.perUserDriveChart, this.perUserDriveChart,
this.apRequestChart, this.apRequestChart,
]; ];
}
public async run() {
// 20分おきにメモリ情報をDBに書き込み // 20分おきにメモリ情報をDBに書き込み
setInterval(() => { this.saveIntervalId = setInterval(() => {
for (const chart of charts) { for (const chart of this.charts) {
chart.save(); chart.save();
} }
}, 1000 * 60 * 20); }, 1000 * 60 * 20);
}
beforeShutdown(() => Promise.all(charts.map(chart => chart.save())));
async onApplicationShutdown(signal: string): Promise<void> {
clearInterval(this.saveIntervalId);
await Promise.all(
this.charts.map(chart => chart.save()),
);
} }
} }

View file

@ -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);