refactor(backend): refactor ChartManagementService
This commit is contained in:
		
							parent
							
								
									72253a1029
								
							
						
					
					
						commit
						3010dc207a
					
				
					 2 changed files with 19 additions and 105 deletions
				
			
		|  | @ -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()), | ||||||
|  | 		); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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); |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue