perf(simulator): compile payload validation JSON schema only once
authorJérôme Benoit <jerome.benoit@sap.com>
Tue, 7 Nov 2023 18:48:46 +0000 (19:48 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Tue, 7 Nov 2023 18:48:46 +0000 (19:48 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ocpp/OCPPIncomingRequestService.ts
src/charging-station/ocpp/OCPPRequestService.ts
src/charging-station/ocpp/OCPPResponseService.ts

index 85fd88e70ebaa624ef454a80493e52cc1dd8f748..0c5b5c61b05651d7859cd8225edabe7d2c5ef92f 100644 (file)
@@ -1,6 +1,6 @@
 import { AsyncResource } from 'node:async_hooks';
 
-import Ajv, { type JSONSchemaType } from 'ajv';
+import Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv';
 import ajvFormats from 'ajv-formats';
 
 import { OCPPConstants } from './OCPPConstants';
@@ -23,6 +23,7 @@ export abstract class OCPPIncomingRequestService extends AsyncResource {
   private static instance: OCPPIncomingRequestService | null = null;
   private readonly version: OCPPVersion;
   private readonly ajv: Ajv;
+  private jsonValidateFunctions: Map<IncomingRequestCommand, ValidateFunction<JsonObject>>;
   protected abstract jsonSchemas: Map<IncomingRequestCommand, JSONSchemaType<JsonObject>>;
 
   protected constructor(version: OCPPVersion) {
@@ -33,6 +34,7 @@ export abstract class OCPPIncomingRequestService extends AsyncResource {
       multipleOfPrecision: 2,
     });
     ajvFormats(this.ajv);
+    this.jsonValidateFunctions = new Map<IncomingRequestCommand, ValidateFunction<JsonObject>>();
     this.incomingRequestHandler = this.incomingRequestHandler.bind(this) as <
       ReqType extends JsonType,
       // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -91,7 +93,10 @@ export abstract class OCPPIncomingRequestService extends AsyncResource {
     if (chargingStation.getOcppStrictCompliance() === false) {
       return true;
     }
-    const validate = this.ajv.compile(schema);
+    if (this.jsonValidateFunctions.has(commandName) === false) {
+      this.jsonValidateFunctions.set(commandName, this.ajv.compile<JsonObject>(schema).bind(this));
+    }
+    const validate = this.jsonValidateFunctions.get(commandName)!;
     if (validate(payload)) {
       return true;
     }
index 18aa5ffdb3cdbeee1b033521128cfc7f161d4aab..52a8a6e5246eb5f06df450f4ed320589129bf57b 100644 (file)
@@ -1,4 +1,4 @@
-import Ajv, { type JSONSchemaType } from 'ajv';
+import Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv';
 import ajvFormats from 'ajv-formats';
 
 import { OCPPConstants } from './OCPPConstants';
@@ -44,6 +44,7 @@ export abstract class OCPPRequestService {
   private readonly version: OCPPVersion;
   private readonly ajv: Ajv;
   private readonly ocppResponseService: OCPPResponseService;
+  private readonly jsonValidateFunctions: Map<RequestCommand, ValidateFunction<JsonObject>>;
   protected abstract jsonSchemas: Map<RequestCommand, JSONSchemaType<JsonObject>>;
 
   protected constructor(version: OCPPVersion, ocppResponseService: OCPPResponseService) {
@@ -53,6 +54,7 @@ export abstract class OCPPRequestService {
       multipleOfPrecision: 2,
     });
     ajvFormats(this.ajv);
+    this.jsonValidateFunctions = new Map<RequestCommand, ValidateFunction<JsonObject>>();
     this.ocppResponseService = ocppResponseService;
     this.requestHandler = this.requestHandler.bind(this) as <
       // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -210,7 +212,13 @@ export abstract class OCPPRequestService {
       );
       return true;
     }
-    const validate = this.ajv.compile(this.jsonSchemas.get(commandName as RequestCommand)!);
+    if (this.jsonValidateFunctions.has(commandName as RequestCommand) === false) {
+      this.jsonValidateFunctions.set(
+        commandName as RequestCommand,
+        this.ajv.compile(this.jsonSchemas.get(commandName as RequestCommand)!).bind(this),
+      );
+    }
+    const validate = this.jsonValidateFunctions.get(commandName as RequestCommand)!;
     payload = cloneObject<T>(payload);
     OCPPServiceUtils.convertDateToISOString<T>(payload);
     if (validate(payload)) {
@@ -247,11 +255,25 @@ export abstract class OCPPRequestService {
       );
       return true;
     }
-    const validate = this.ajv.compile(
-      this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(
+    if (
+      this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(
         commandName as IncomingRequestCommand,
-      )!,
-    );
+      ) === false
+    ) {
+      this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.set(
+        commandName as IncomingRequestCommand,
+        this.ajv
+          .compile(
+            this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(
+              commandName as IncomingRequestCommand,
+            )!,
+          )
+          .bind(this),
+      );
+    }
+    const validate = this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(
+      commandName as IncomingRequestCommand,
+    )!;
     payload = cloneObject<T>(payload);
     OCPPServiceUtils.convertDateToISOString<T>(payload);
     if (validate(payload)) {
index 9daa991c952c30ff184748099dfbe158544ed8c1..46d12fb048aa7374972316451972a4090aef59a2 100644 (file)
@@ -1,4 +1,4 @@
-import Ajv, { type JSONSchemaType } from 'ajv';
+import Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv';
 import ajvFormats from 'ajv-formats';
 
 import { OCPPServiceUtils } from './OCPPServiceUtils';
@@ -17,8 +17,16 @@ const moduleName = 'OCPPResponseService';
 
 export abstract class OCPPResponseService {
   private static instance: OCPPResponseService | null = null;
+
+  public jsonIncomingRequestResponseValidateFunctions: Map<
+    IncomingRequestCommand,
+    ValidateFunction<JsonObject>
+  >;
+
   private readonly version: OCPPVersion;
   private readonly ajv: Ajv;
+  private jsonRequestValidateFunctions: Map<RequestCommand, ValidateFunction<JsonObject>>;
+
   public abstract jsonIncomingRequestResponseSchemas: Map<
     IncomingRequestCommand,
     JSONSchemaType<JsonObject>
@@ -31,6 +39,11 @@ export abstract class OCPPResponseService {
       multipleOfPrecision: 2,
     });
     ajvFormats(this.ajv);
+    this.jsonRequestValidateFunctions = new Map<RequestCommand, ValidateFunction<JsonObject>>();
+    this.jsonIncomingRequestResponseValidateFunctions = new Map<
+      IncomingRequestCommand,
+      ValidateFunction<JsonObject>
+    >();
     this.responseHandler = this.responseHandler.bind(this) as <
       ReqType extends JsonType,
       ResType extends JsonType,
@@ -64,7 +77,13 @@ export abstract class OCPPResponseService {
     if (chargingStation.getOcppStrictCompliance() === false) {
       return true;
     }
-    const validate = this.ajv.compile(schema);
+    if (this.jsonRequestValidateFunctions.has(commandName) === false) {
+      this.jsonRequestValidateFunctions.set(
+        commandName,
+        this.ajv.compile<JsonObject>(schema).bind(this),
+      );
+    }
+    const validate = this.jsonRequestValidateFunctions.get(commandName)!;
     if (validate(payload)) {
       return true;
     }