feat: add graceful shutdown
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 25 May 2023 22:02:19 +0000 (00:02 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 25 May 2023 22:02:19 +0000 (00:02 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/Bootstrap.ts
src/charging-station/ChargingStationWorker.ts
src/charging-station/ui-server/AbstractUIServer.ts
src/charging-station/ui-server/ui-services/AbstractUIService.ts
src/worker/WorkerUtils.ts

index 3312224278521fe7e98fc128b58c2ad5bc887fb6..4a6bd75c2d7a2ebbce2e6ebb24c3ceab9019dba6 100644 (file)
@@ -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<void> {
     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<void> => {
+    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 |');
   };
index 24ee468c0a5cc1244cd52890fc279d07fde70df7..60d065642c7ef94585aa6902bf839354339dc6aa 100644 (file)
@@ -35,7 +35,6 @@ export let threadWorker: ThreadWorker;
 if (Configuration.workerPoolInUse()) {
   threadWorker = new ThreadWorker<ChargingStationWorkerData>(startChargingStation, {
     maxInactiveTime: WorkerConstants.POOL_MAX_INACTIVE_TIME,
-    async: false,
   });
 } else {
   // Add message listener to start charging station from main thread
index 643b18831f2010c525844ccd5085f1a1a3a20ca8..2f6f950b938fd94e74557e4b4f3206d5ae0e2939 100644 (file)
@@ -46,6 +46,16 @@ export abstract class AbstractUIServer {
     this.chargingStations.clear();
   }
 
+  public async sendBroadcastChannelRequest(
+    id: string,
+    procedureName: ProcedureName,
+    requestPayload: RequestPayload
+  ): Promise<void> {
+    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);
index 71b057abe4a2f7369c1376d97ffcc55046eb0e89..541966afbc98f2ad9317d126185ac15e5e4eace8 100644 (file)
@@ -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,
index 7c72ff3e57fcac90ed51150f7780727a754df808..943de290800f0b1804e4366da5c2d76ab2d6f28e 100644 (file)
@@ -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()}`));
     }
   };