From 6bd808fd1dc554c8d55521b20a064447835ef04c Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 26 May 2023 00:02:19 +0200 Subject: [PATCH] feat: add graceful shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/Bootstrap.ts | 23 ++++++++++++++++++- src/charging-station/ChargingStationWorker.ts | 1 - .../ui-server/AbstractUIServer.ts | 10 ++++++++ .../ui-services/AbstractUIService.ts | 2 +- src/worker/WorkerUtils.ts | 8 +++++-- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index 33122242..4a6bd75c 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -17,10 +17,11 @@ import { type ChargingStationWorkerMessage, type ChargingStationWorkerMessageData, ChargingStationWorkerMessageEvents, + ProcedureName, type StationTemplateUrl, type Statistics, } from '../types'; -import { Configuration, ErrorUtils, Utils, logger } from '../utils'; +import { Configuration, Constants, ErrorUtils, Utils, logger } from '../utils'; import { type MessageHandler, type WorkerAbstract, WorkerFactory } from '../worker'; const moduleName = 'Bootstrap'; @@ -44,6 +45,11 @@ export class Bootstrap { private readonly workerScript: string; private constructor() { + for (const signal of ['SIGINT', 'SIGQUIT', 'SIGTERM']) { + process.on(signal, () => { + this.gracefulShutdown().catch(Constants.EMPTY_FUNCTION); + }); + } // Enable unconditionally for now ErrorUtils.handleUnhandledRejection(); ErrorUtils.handleUncaughtException(); @@ -123,6 +129,11 @@ export class Bootstrap { public async stop(): Promise { if (isMainThread && this.started === true) { + await this.uiServer?.sendBroadcastChannelRequest( + Utils.generateUUID(), + ProcedureName.STOP_CHARGING_STATION, + Constants.EMPTY_FREEZED_OBJECT + ); await this.workerImplementation?.stop(); this.workerImplementation = null; this.uiServer?.stop(); @@ -271,6 +282,16 @@ export class Bootstrap { }); } + private gracefulShutdown = async (): Promise => { + console.info(`${chalk.green('Graceful shutdown')}`); + try { + await this.stop(); + process.exit(0); + } catch (error) { + process.exit(1); + } + }; + private logPrefix = (): string => { return Utils.logPrefix(' Bootstrap |'); }; diff --git a/src/charging-station/ChargingStationWorker.ts b/src/charging-station/ChargingStationWorker.ts index 24ee468c..60d06564 100644 --- a/src/charging-station/ChargingStationWorker.ts +++ b/src/charging-station/ChargingStationWorker.ts @@ -35,7 +35,6 @@ export let threadWorker: ThreadWorker; if (Configuration.workerPoolInUse()) { threadWorker = new ThreadWorker(startChargingStation, { maxInactiveTime: WorkerConstants.POOL_MAX_INACTIVE_TIME, - async: false, }); } else { // Add message listener to start charging station from main thread diff --git a/src/charging-station/ui-server/AbstractUIServer.ts b/src/charging-station/ui-server/AbstractUIServer.ts index 643b1883..2f6f950b 100644 --- a/src/charging-station/ui-server/AbstractUIServer.ts +++ b/src/charging-station/ui-server/AbstractUIServer.ts @@ -46,6 +46,16 @@ export abstract class AbstractUIServer { this.chargingStations.clear(); } + public async sendBroadcastChannelRequest( + id: string, + procedureName: ProcedureName, + requestPayload: RequestPayload + ): Promise { + for (const uiService of this.uiServices.values()) { + await uiService.requestHandler(this.buildProtocolRequest(id, procedureName, requestPayload)); + } + } + protected startHttpServer(): void { if (this.httpServer.listening === false) { this.httpServer.listen(this.uiServerConfiguration.options); diff --git a/src/charging-station/ui-server/ui-services/AbstractUIService.ts b/src/charging-station/ui-server/ui-services/AbstractUIService.ts index 71b057ab..541966af 100644 --- a/src/charging-station/ui-server/ui-services/AbstractUIService.ts +++ b/src/charging-station/ui-server/ui-services/AbstractUIService.ts @@ -87,7 +87,7 @@ export abstract class AbstractUIService { responsePayload = await this.requestHandlers.get(command)(messageId, command, requestPayload); } catch (error) { // Log - logger.error(`${this.logPrefix(moduleName, 'messageHandler')} Handle request error:`, error); + logger.error(`${this.logPrefix(moduleName, 'requestHandler')} Handle request error:`, error); responsePayload = { hashIds: requestPayload?.hashIds, status: ResponseStatus.FAILURE, diff --git a/src/worker/WorkerUtils.ts b/src/worker/WorkerUtils.ts index 7c72ff3e..943de290 100644 --- a/src/worker/WorkerUtils.ts +++ b/src/worker/WorkerUtils.ts @@ -10,8 +10,12 @@ export class WorkerUtils { } public static defaultExitHandler = (code: number): void => { - if (code !== 0) { - console.error(chalk.red(`Worker exited with error exit code: ${code.toString()}`)); + if (code === 0) { + console.info(chalk.green('Worker exited successfully')); + } else if (code === 1) { + console.info(chalk.green('Worker terminated successfully')); + } else if (code > 1) { + console.error(chalk.red(`Worker exited with exit code: ${code.toString()}`)); } }; -- 2.34.1