fix: fix performance configuration change at runtime
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 27 Nov 2023 20:12:32 +0000 (21:12 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 27 Nov 2023 20:12:32 +0000 (21:12 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/assets/config-template.json
src/charging-station/Bootstrap.ts
src/charging-station/ui-server/UIServerFactory.ts
src/performance/storage/StorageFactory.ts
src/worker/WorkerFactory.ts

index 6b06f2bf56a7da91c05ce4c7d5fe8cf065a34e36..5e18d942fd50ccdba139eec8db162e5e6811590c 100644 (file)
@@ -11,7 +11,7 @@
     "processType": "workerSet"
   },
   "performanceStorage": {
-    "enabled": true,
+    "enabled": false,
     "type": "jsonfile"
   },
   "uiServer": {
index 003b093a3579c02d094addc7f2210e88d14f789b..710d2c894d485b4abcb4193f3721df58bbad4264 100644 (file)
@@ -55,16 +55,16 @@ export class Bootstrap extends EventEmitter {
   private static instance: Bootstrap | null = null;
   public numberOfChargingStations!: number;
   public numberOfChargingStationTemplates!: number;
-  private workerImplementation: WorkerAbstract<ChargingStationWorkerData> | null;
-  private readonly uiServer: AbstractUIServer | null;
-  private readonly storage!: Storage;
+  private workerConfiguration?: WorkerConfiguration;
+  private workerImplementation?: WorkerAbstract<ChargingStationWorkerData>;
+  private readonly uiServer?: AbstractUIServer;
+  private storage?: Storage;
   private numberOfStartedChargingStations!: number;
   private readonly version: string = version;
   private initializedCounters: boolean;
   private started: boolean;
   private starting: boolean;
   private stopping: boolean;
-  private readonly workerScript: string;
 
   private constructor() {
     super();
@@ -79,24 +79,16 @@ export class Bootstrap extends EventEmitter {
     this.stopping = false;
     this.initializedCounters = false;
     this.initializeCounters();
-    this.workerImplementation = null;
-    this.workerScript = join(
-      dirname(fileURLToPath(import.meta.url)),
-      `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`,
-    );
     this.uiServer = UIServerFactory.getUIServerImplementation(
       Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer),
     );
-    const performanceStorageConfiguration =
-      Configuration.getConfigurationSection<StorageConfiguration>(
-        ConfigurationSection.performanceStorage,
-      );
-    performanceStorageConfiguration.enabled === true &&
-      (this.storage = StorageFactory.getStorage(
-        performanceStorageConfiguration.type!,
-        performanceStorageConfiguration.uri!,
-        this.logPrefix(),
-      ));
+    this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted);
+    this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped);
+    this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated);
+    this.on(
+      ChargingStationWorkerMessageEvents.performanceStatistics,
+      this.workerEventPerformanceStatistics,
+    );
     Configuration.configurationChangeCallback = async () => Bootstrap.getInstance().restart(false);
   }
 
@@ -112,12 +104,23 @@ export class Bootstrap extends EventEmitter {
       if (this.starting === false) {
         this.starting = true;
         this.initializeCounters();
-        const workerConfiguration = Configuration.getConfigurationSection<WorkerConfiguration>(
+        this.workerConfiguration = Configuration.getConfigurationSection<WorkerConfiguration>(
           ConfigurationSection.worker,
         );
-        this.initializeWorkerImplementation(workerConfiguration);
+        this.initializeWorkerImplementation(this.workerConfiguration);
         await this.workerImplementation?.start();
-        await this.storage?.open();
+        const performanceStorageConfiguration =
+          Configuration.getConfigurationSection<StorageConfiguration>(
+            ConfigurationSection.performanceStorage,
+          );
+        if (performanceStorageConfiguration.enabled === true) {
+          this.storage = StorageFactory.getStorage(
+            performanceStorageConfiguration.type!,
+            performanceStorageConfiguration.uri!,
+            this.logPrefix(),
+          );
+          await this.storage?.open();
+        }
         Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
           .enabled === true && this.uiServer?.start();
         // Start ChargingStation object instance in worker thread
@@ -142,13 +145,13 @@ export class Bootstrap extends EventEmitter {
               this.version
             } started with ${this.numberOfChargingStations.toString()} charging station(s) from ${this.numberOfChargingStationTemplates.toString()} configured charging station template(s) and ${
               Configuration.workerDynamicPoolInUse()
-                ? `${workerConfiguration.poolMinSize?.toString()}/`
+                ? `${this.workerConfiguration.poolMinSize?.toString()}/`
                 : ''
             }${this.workerImplementation?.size}${
               Configuration.workerPoolInUse()
-                ? `/${workerConfiguration.poolMaxSize?.toString()}`
+                ? `/${this.workerConfiguration.poolMaxSize?.toString()}`
                 : ''
-            } worker(s) concurrently running in '${workerConfiguration.processType}' mode${
+            } worker(s) concurrently running in '${this.workerConfiguration.processType}' mode${
               !isNullOrUndefined(this.workerImplementation?.maxElementsPerWorker)
                 ? ` (${this.workerImplementation?.maxElementsPerWorker} charging station(s) per worker)`
                 : ''
@@ -191,9 +194,11 @@ export class Bootstrap extends EventEmitter {
           }
         }
         await this.workerImplementation?.stop();
-        this.workerImplementation = null;
+        delete this.workerImplementation;
+        delete this.workerConfiguration;
         this.uiServer?.stop();
         await this.storage?.close();
+        delete this.storage;
         this.resetCounters();
         this.initializedCounters = false;
         this.started = false;
@@ -243,21 +248,23 @@ export class Bootstrap extends EventEmitter {
           ? Math.round(this.numberOfChargingStations / (availableParallelism() * 1.5))
           : 1;
     }
-    this.workerImplementation === null &&
-      (this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
-        this.workerScript,
-        workerConfiguration.processType!,
-        {
-          workerStartDelay: workerConfiguration.startDelay,
-          elementStartDelay: workerConfiguration.elementStartDelay,
-          poolMaxSize: workerConfiguration.poolMaxSize!,
-          poolMinSize: workerConfiguration.poolMinSize!,
-          elementsPerWorker: elementsPerWorker ?? (workerConfiguration.elementsPerWorker as number),
-          poolOptions: {
-            messageHandler: this.messageHandler.bind(this) as (message: unknown) => void,
-          },
+    this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
+      join(
+        dirname(fileURLToPath(import.meta.url)),
+        `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`,
+      ),
+      workerConfiguration.processType!,
+      {
+        workerStartDelay: workerConfiguration.startDelay,
+        elementStartDelay: workerConfiguration.elementStartDelay,
+        poolMaxSize: workerConfiguration.poolMaxSize!,
+        poolMinSize: workerConfiguration.poolMinSize!,
+        elementsPerWorker: elementsPerWorker ?? (workerConfiguration.elementsPerWorker as number),
+        poolOptions: {
+          messageHandler: this.messageHandler.bind(this) as (message: unknown) => void,
         },
-      ));
+      },
+    );
   }
 
   private messageHandler(
@@ -273,19 +280,15 @@ export class Bootstrap extends EventEmitter {
     try {
       switch (msg.event) {
         case ChargingStationWorkerMessageEvents.started:
-          this.workerEventStarted(msg.data as ChargingStationData);
           this.emit(ChargingStationWorkerMessageEvents.started, msg.data as ChargingStationData);
           break;
         case ChargingStationWorkerMessageEvents.stopped:
-          this.workerEventStopped(msg.data as ChargingStationData);
           this.emit(ChargingStationWorkerMessageEvents.stopped, msg.data as ChargingStationData);
           break;
         case ChargingStationWorkerMessageEvents.updated:
-          this.workerEventUpdated(msg.data as ChargingStationData);
           this.emit(ChargingStationWorkerMessageEvents.updated, msg.data as ChargingStationData);
           break;
         case ChargingStationWorkerMessageEvents.performanceStatistics:
-          this.workerEventPerformanceStatistics(msg.data as Statistics);
           this.emit(
             ChargingStationWorkerMessageEvents.performanceStatistics,
             msg.data as Statistics,
@@ -346,7 +349,7 @@ export class Bootstrap extends EventEmitter {
   };
 
   private workerEventPerformanceStatistics = (data: Statistics) => {
-    this.storage.storePerformanceStatistics(data) as void;
+    this.storage?.storePerformanceStatistics(data) as void;
   };
 
   private initializeCounters() {
index 0f75f60feabdd9f331bc56b3b6db3ec13c5c4c58..d3c34b58aedaaa48f25289d011d84a4ba3cec942 100644 (file)
@@ -17,7 +17,7 @@ export class UIServerFactory {
 
   public static getUIServerImplementation(
     uiServerConfiguration: UIServerConfiguration,
-  ): AbstractUIServer | null {
+  ): AbstractUIServer | undefined {
     if (UIServerUtils.isLoopback(uiServerConfiguration.options!.host!) === false) {
       console.warn(
         chalk.yellow(
@@ -45,8 +45,6 @@ export class UIServerFactory {
         return new UIWebSocketServer(uiServerConfiguration);
       case ApplicationProtocol.HTTP:
         return new UIHttpServer(uiServerConfiguration);
-      default:
-        return null;
     }
   }
 }
index cecaa48901bf3689475503d431891edd9e41c518..31d0982e7b99682ee6862b518413a60dbf3a6c31 100644 (file)
@@ -13,8 +13,12 @@ export class StorageFactory {
     // This is intentional
   }
 
-  public static getStorage(type: StorageType, connectionUri: string, logPrefix: string): Storage {
-    let storageInstance: Storage | null = null;
+  public static getStorage(
+    type: StorageType,
+    connectionUri: string,
+    logPrefix: string,
+  ): Storage | undefined {
+    let storageInstance: Storage;
     switch (type) {
       case StorageType.JSON_FILE:
         storageInstance = new JsonFileStorage(connectionUri, logPrefix);
index d248d3bab765b5aabad6e9d5e2afb5dc773c5df0..608e879859b9662cb093241aec71fccd7d0a9ea1 100644 (file)
@@ -16,12 +16,12 @@ export class WorkerFactory {
     workerScript: string,
     workerProcessType: WorkerProcessType,
     workerOptions?: WorkerOptions,
-  ): WorkerAbstract<T> | null {
+  ): WorkerAbstract<T> | undefined {
     if (!isMainThread) {
       throw new Error('Cannot get a worker implementation outside the main thread');
     }
     workerOptions = { ...DEFAULT_WORKER_OPTIONS, ...workerOptions };
-    let workerImplementation: WorkerAbstract<T> | null = null;
+    let workerImplementation: WorkerAbstract<T>;
     switch (workerProcessType) {
       case WorkerProcessType.workerSet:
         workerImplementation = new WorkerSet(workerScript, workerOptions);