From aaf2bf9c2ec7712394d7ab9da12449262d894e60 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 24 Mar 2023 22:19:06 +0100 Subject: [PATCH] perf(simulator): use content addressable cache for idtag distribution MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/AuthorizedTagsCache.ts | 66 ++++++++++++++++++- .../AutomaticTransactionGenerator.ts | 44 ++----------- 2 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/charging-station/AuthorizedTagsCache.ts b/src/charging-station/AuthorizedTagsCache.ts index 48c72fd4..b63387ed 100644 --- a/src/charging-station/AuthorizedTagsCache.ts +++ b/src/charging-station/AuthorizedTagsCache.ts @@ -1,6 +1,7 @@ import fs from 'node:fs'; -import { FileType } from '../types'; +import { type ChargingStation, ChargingStationUtils } from './internal'; +import { FileType, IdTagDistribution } from '../types'; import { FileUtils, Utils, logger } from '../utils'; type TagsCacheValueType = { @@ -11,9 +12,11 @@ type TagsCacheValueType = { export class AuthorizedTagsCache { private static instance: AuthorizedTagsCache | null = null; private readonly tagsCaches: Map; + private readonly tagsCachesAddressableIndexes: Map; private constructor() { this.tagsCaches = new Map(); + this.tagsCachesAddressableIndexes = new Map(); } public static getInstance(): AuthorizedTagsCache { @@ -23,6 +26,27 @@ export class AuthorizedTagsCache { return AuthorizedTagsCache.instance; } + public getIdTag( + distribution: IdTagDistribution, + chargingStation: ChargingStation, + connectorId: number + ): string { + const hashId = chargingStation.stationInfo.hashId; + const authorizationFile = ChargingStationUtils.getAuthorizationFile( + chargingStation.stationInfo + ); + switch (distribution) { + case IdTagDistribution.RANDOM: + return this.getRandomIdTag(hashId, authorizationFile); + case IdTagDistribution.ROUND_ROBIN: + return this.getRoundRobinIdTag(hashId, authorizationFile); + case IdTagDistribution.CONNECTOR_AFFINITY: + return this.getConnectorAffinityIdTag(chargingStation, connectorId); + default: + return this.getRoundRobinIdTag(hashId, authorizationFile); + } + } + public getAuthorizedTags(file: string): string[] | undefined { if (this.hasTags(file) === false) { this.setTags(file, this.getAuthorizedTagsFromFile(file)); @@ -34,6 +58,37 @@ export class AuthorizedTagsCache { return this.deleteTags(file); } + private getRandomIdTag(hashId: string, file: string): string { + const tags = this.getAuthorizedTags(file); + this.tagsCachesAddressableIndexes.set( + file + hashId, + Math.floor(Utils.secureRandom() * tags.length) + ); + return tags[this.tagsCachesAddressableIndexes.get(file + hashId)]; + } + + private getRoundRobinIdTag(hashId: string, file: string): string { + const tags = this.getAuthorizedTags(file); + const idTagIndex = this.tagsCachesAddressableIndexes.get(file + hashId) ?? 0; + const idTag = tags[idTagIndex]; + this.tagsCachesAddressableIndexes.set( + file + hashId, + idTagIndex === tags.length - 1 ? 0 : idTagIndex + 1 + ); + return idTag; + } + + private getConnectorAffinityIdTag(chargingStation: ChargingStation, connectorId: number): string { + const file = ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo); + const tags = this.getAuthorizedTags(file); + const hashId = chargingStation.stationInfo.hashId; + this.tagsCachesAddressableIndexes.set( + file + hashId, + (chargingStation.index - 1 + (connectorId - 1)) % tags.length + ); + return tags[this.tagsCachesAddressableIndexes.get(file + hashId)]; + } + private hasTags(file: string): boolean { return this.tagsCaches.has(file); } @@ -53,6 +108,7 @@ export class AuthorizedTagsCache { `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload` ); this.deleteTags(file); + this.deleteTagsIndexes(file); } catch (error) { FileUtils.handleFileException( file, @@ -79,6 +135,14 @@ export class AuthorizedTagsCache { return this.tagsCaches.delete(file); } + private deleteTagsIndexes(file: string): void { + for (const [key] of this.tagsCachesAddressableIndexes) { + if (key.startsWith(file)) { + this.tagsCachesAddressableIndexes.delete(key); + } + } + } + private getAuthorizedTagsFromFile(file: string): string[] { let authorizedTags: string[] = []; if (file) { diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index c6056dd5..aa2012df 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -2,7 +2,7 @@ import { AsyncResource } from 'node:async_hooks'; -import { type ChargingStation, ChargingStationUtils } from './internal'; +import { AuthorizedTagsCache, type ChargingStation, ChargingStationUtils } from './internal'; import { BaseError } from '../exception'; // import { PerformanceStatistics } from '../performance'; import { PerformanceStatistics } from '../performance/PerformanceStatistics'; @@ -12,7 +12,6 @@ import { type AuthorizeResponse, type AutomaticTransactionGeneratorConfiguration, ConnectorStatusEnum, - IdTagDistribution, RequestCommand, type StartTransactionRequest, type StartTransactionResponse, @@ -325,7 +324,11 @@ export class AutomaticTransactionGenerator extends AsyncResource { const beginId = PerformanceStatistics.beginMeasure(measureId); let startResponse: StartTransactionResponse; if (this.chargingStation.hasAuthorizedTags()) { - const idTag = this.getIdTag(connectorId); + const idTag = AuthorizedTagsCache.getInstance().getIdTag( + this.configuration?.idTagDistribution, + this.chargingStation, + connectorId + ); const startTransactionLogMsg = `${this.logPrefix( connectorId )} start transaction with an idTag '${idTag}'`; @@ -413,41 +416,6 @@ export class AutomaticTransactionGenerator extends AsyncResource { return this.configuration?.requireAuthorize ?? true; } - private getRandomIdTag(authorizationFile: string): string { - const tags = this.chargingStation.authorizedTagsCache.getAuthorizedTags(authorizationFile); - this.idTagIndex = Math.floor(Utils.secureRandom() * tags.length); - return tags[this.idTagIndex]; - } - - private getRoundRobinIdTag(authorizationFile: string): string { - const tags = this.chargingStation.authorizedTagsCache.getAuthorizedTags(authorizationFile); - const idTag = tags[this.idTagIndex]; - this.idTagIndex = this.idTagIndex === tags.length - 1 ? 0 : this.idTagIndex + 1; - return idTag; - } - - private getConnectorAffinityIdTag(authorizationFile: string, connectorId: number): string { - const tags = this.chargingStation.authorizedTagsCache.getAuthorizedTags(authorizationFile); - this.idTagIndex = (this.chargingStation.index - 1 + (connectorId - 1)) % tags.length; - return tags[this.idTagIndex]; - } - - private getIdTag(connectorId: number): string { - const authorizationFile = ChargingStationUtils.getAuthorizationFile( - this.chargingStation.stationInfo - ); - switch (this.configuration?.idTagDistribution) { - case IdTagDistribution.RANDOM: - return this.getRandomIdTag(authorizationFile); - case IdTagDistribution.ROUND_ROBIN: - return this.getRoundRobinIdTag(authorizationFile); - case IdTagDistribution.CONNECTOR_AFFINITY: - return this.getConnectorAffinityIdTag(authorizationFile, connectorId); - default: - return this.getRoundRobinIdTag(authorizationFile); - } - } - private logPrefix = (connectorId?: number): string => { return Utils.logPrefix( ` ${this.chargingStation.stationInfo.chargingStationId} | ATG${ -- 2.34.1