From 6fd7108a51030e65432666eeb4171c9dcfdd3662 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Thu, 27 Feb 2025 13:59:29 +0100 Subject: [PATCH 1/6] feat(ZMSKVR-138): add second level caching to offices and service --- .ddev/.env.template | 2 +- .../OfficesServicesRelationsService.php | 29 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/.ddev/.env.template b/.ddev/.env.template index b0085cad6..c6e34154e 100644 --- a/.ddev/.env.template +++ b/.ddev/.env.template @@ -38,7 +38,7 @@ ALTCHA_CAPTCHA_ENDPOINT=https://eu.altcha.org/form/ ALTCHA_CAPTCHA_ENDPOINT_PUZZLE=https://eu.altcha.org/ # Rate Limiting -RATE_LIMIT_MAX_REQUESTS=60 +RATE_LIMIT_MAX_REQUESTS=10000000 RATE_LIMIT_CACHE_TTL=60 RATE_LIMIT_MAX_RETRIES=3 RATE_LIMIT_BACKOFF_MIN=10 diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php index 22de24e63..94533c8e1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php @@ -1,10 +1,10 @@ getServicesAndOffices($showUnpublished); } - private function getServicesAndOffices(bool $showUnpublished): array|OfficeServiceAndRelationList + private function getServicesAndOffices(bool $showUnpublished = false): array|OfficeServiceAndRelationList { - return ZmsApiFacadeService::getServicesAndOffices($showUnpublished); + // Include showUnpublished in the cache key to differentiate between published/unpublished results + $cacheKey = 'processed_offices_and_services_' . ($showUnpublished ? 'with_unpublished' : 'published_only'); + + // Try to get the processed data from cache first + if (\App::$cache && ($processedData = \App::$cache->get($cacheKey))) { + return $processedData; + } + + // If not in cache, get the transformed data (which internally uses the source_dldb cache) + $result = ZmsApiFacadeService::getServicesAndOffices($showUnpublished); + + // Cache the processed result with the same TTL as source cache + if (\App::$cache && !is_array($result)) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Cache set for processed data', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL, + 'show_unpublished' => $showUnpublished + ]); + } + + return $result; } -} +} \ No newline at end of file From e3d97eeab26161b3183d0852cdb2e7af25ad14d0 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Thu, 27 Feb 2025 14:41:44 +0100 Subject: [PATCH 2/6] feat(ZMSKVR-138): add second level caching to offices and service --- .ddev/.env.template | 2 +- .../Services/Core/ZmsApiFacadeService.php | 32 +++++++++++++++++-- .../OfficesServicesRelationsService.php | 29 +++-------------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/.ddev/.env.template b/.ddev/.env.template index c6e34154e..b0085cad6 100644 --- a/.ddev/.env.template +++ b/.ddev/.env.template @@ -38,7 +38,7 @@ ALTCHA_CAPTCHA_ENDPOINT=https://eu.altcha.org/form/ ALTCHA_CAPTCHA_ENDPOINT_PUZZLE=https://eu.altcha.org/ # Rate Limiting -RATE_LIMIT_MAX_REQUESTS=10000000 +RATE_LIMIT_MAX_REQUESTS=60 RATE_LIMIT_CACHE_TTL=60 RATE_LIMIT_MAX_RETRIES=3 RATE_LIMIT_BACKOFF_MIN=10 diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php index 4be687629..5abff68c9 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php @@ -34,6 +34,13 @@ */ class ZmsApiFacadeService { + private const CACHE_KEY_OFFICES = 'processed_offices'; + private const CACHE_KEY_SCOPES = 'processed_scopes'; + private const CACHE_KEY_SERVICES = 'processed_services'; + private const CACHE_KEY_OFFICES_AND_SERVICES = 'processed_offices_and_services'; + private const CACHE_KEY_OFFICES_BY_SERVICE_PREFIX = 'processed_offices_by_service_'; + private const CACHE_KEY_SERVICES_BY_OFFICE_PREFIX = 'processed_services_by_office_'; + private static ?string $currentLanguage = null; public static function setLanguageContext(?string $language): void { @@ -153,10 +160,19 @@ public static function getServices(bool $showUnpublished = false): ServiceList|a public static function getServicesAndOffices(bool $showUnpublished = false): OfficeServiceAndRelationList|array { + // Include showUnpublished in the cache key to differentiate cached results + $cacheKey = self::CACHE_KEY_OFFICES_AND_SERVICES . ($showUnpublished ? '_unpublished' : ''); + + // Check second-level cache first + if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { + return $cachedData; + } + + // Original implementation with showUnpublished parameter preserved $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); $requestList = ZmsApiClientService::getServices() ?? new RequestList(); $relationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); - + $offices = MapperService::mapOfficesWithScope($providerList, $showUnpublished) ?? new OfficeList(); $services = MapperService::mapServicesWithCombinations( $requestList, @@ -164,7 +180,19 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off $showUnpublished ) ?? new ServiceList(); $relations = MapperService::mapRelations($relationList) ?? new OfficeServiceRelationList(); - return new OfficeServiceAndRelationList($offices, $services, $relations); + + $result = new OfficeServiceAndRelationList($offices, $services, $relations); + + // Store in second-level cache + if (\App::$cache) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + + return $result; } /* Todo add method diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php index 94533c8e1..22de24e63 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Office/OfficesServicesRelationsService.php @@ -1,10 +1,10 @@ getServicesAndOffices($showUnpublished); } - private function getServicesAndOffices(bool $showUnpublished = false): array|OfficeServiceAndRelationList + private function getServicesAndOffices(bool $showUnpublished): array|OfficeServiceAndRelationList { - // Include showUnpublished in the cache key to differentiate between published/unpublished results - $cacheKey = 'processed_offices_and_services_' . ($showUnpublished ? 'with_unpublished' : 'published_only'); - - // Try to get the processed data from cache first - if (\App::$cache && ($processedData = \App::$cache->get($cacheKey))) { - return $processedData; - } - - // If not in cache, get the transformed data (which internally uses the source_dldb cache) - $result = ZmsApiFacadeService::getServicesAndOffices($showUnpublished); - - // Cache the processed result with the same TTL as source cache - if (\App::$cache && !is_array($result)) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Cache set for processed data', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL, - 'show_unpublished' => $showUnpublished - ]); - } - - return $result; + return ZmsApiFacadeService::getServicesAndOffices($showUnpublished); } -} \ No newline at end of file +} From a3601f852c54682b829d251b34af58c4c5d032f5 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Thu, 27 Feb 2025 14:50:18 +0100 Subject: [PATCH 3/6] clean(ZMSKVR-138): php formatting --- .../Services/Core/ZmsApiFacadeService.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php index 5abff68c9..bb63881a8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php @@ -162,17 +162,17 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off { // Include showUnpublished in the cache key to differentiate cached results $cacheKey = self::CACHE_KEY_OFFICES_AND_SERVICES . ($showUnpublished ? '_unpublished' : ''); - + // Check second-level cache first if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { return $cachedData; } - + // Original implementation with showUnpublished parameter preserved $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); $requestList = ZmsApiClientService::getServices() ?? new RequestList(); $relationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); - + $offices = MapperService::mapOfficesWithScope($providerList, $showUnpublished) ?? new OfficeList(); $services = MapperService::mapServicesWithCombinations( $requestList, @@ -180,9 +180,9 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off $showUnpublished ) ?? new ServiceList(); $relations = MapperService::mapRelations($relationList) ?? new OfficeServiceRelationList(); - + $result = new OfficeServiceAndRelationList($offices, $services, $relations); - + // Store in second-level cache if (\App::$cache) { \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); @@ -191,7 +191,7 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off 'ttl' => \App::$SOURCE_CACHE_TTL ]); } - + return $result; } From 7402663194d82c0f5e20f773cf5c142ac9417570 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Thu, 27 Feb 2025 18:41:30 +0100 Subject: [PATCH 4/6] feat(ZMSKVR-138): add second level caching to all dldb methods in zmsapifacadeservice --- .../Services/Core/ZmsApiFacadeService.php | 94 +++++++++++++++++-- 1 file changed, 85 insertions(+), 9 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php index bb63881a8..06c925943 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php @@ -54,6 +54,12 @@ private static function getError(string $key): array public static function getOffices(bool $showUnpublished = false): OfficeList { + $cacheKey = self::CACHE_KEY_OFFICES . ($showUnpublished ? '_unpublished' : ''); + + if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { + return $cachedData; + } + $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); $scopeList = ZmsApiClientService::getScopes() ?? new ScopeList(); $offices = []; @@ -97,11 +103,27 @@ public static function getOffices(bool $showUnpublished = false): OfficeList ); } - return new OfficeList($offices); + $result = new OfficeList($offices); + + if (\App::$cache) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + + return $result; } public static function getScopes(): ThinnedScopeList|array { + $cacheKey = self::CACHE_KEY_SCOPES; + + if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { + return $cachedData; + } + $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); $scopeList = ZmsApiClientService::getScopes() ?? new ScopeList(); $scopeMap = []; @@ -135,11 +157,27 @@ public static function getScopes(): ThinnedScopeList|array } } - return new ThinnedScopeList($scopesProjectionList); + $result = new ThinnedScopeList($scopesProjectionList); + + if (\App::$cache) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + + return $result; } public static function getServices(bool $showUnpublished = false): ServiceList|array { + $cacheKey = self::CACHE_KEY_SERVICES . ($showUnpublished ? '_unpublished' : ''); + + if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { + return $cachedData; + } + $requestList = ZmsApiClientService::getServices() ?? new RequestList(); $services = []; foreach ($requestList as $request) { @@ -155,20 +193,27 @@ public static function getServices(bool $showUnpublished = false): ServiceList|a $services[] = new Service(id: (int) $request->getId(), name: $request->getName(), maxQuantity: $additionalData['maxQuantity'] ?? 1); } - return new ServiceList($services); + $result = new ServiceList($services); + + if (\App::$cache) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + + return $result; } public static function getServicesAndOffices(bool $showUnpublished = false): OfficeServiceAndRelationList|array { - // Include showUnpublished in the cache key to differentiate cached results $cacheKey = self::CACHE_KEY_OFFICES_AND_SERVICES . ($showUnpublished ? '_unpublished' : ''); - // Check second-level cache first if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { return $cachedData; } - // Original implementation with showUnpublished parameter preserved $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); $requestList = ZmsApiClientService::getServices() ?? new RequestList(); $relationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); @@ -183,7 +228,6 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off $result = new OfficeServiceAndRelationList($offices, $services, $relations); - // Store in second-level cache if (\App::$cache) { \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); LoggerService::logInfo('Second-level cache set', [ @@ -260,6 +304,12 @@ public static function getScopeByOfficeId(int $officeId): ThinnedScope|array public static function getOfficeListByServiceId(int $serviceId, bool $showUnpublished = false): OfficeList|array { + $cacheKey = self::CACHE_KEY_OFFICES_BY_SERVICE_PREFIX . $serviceId . ($showUnpublished ? '_unpublished' : ''); + + if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { + return $cachedData; + } + $providerList = ZmsApiClientService::getOffices() ?? new ProviderList(); $requestRelationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); $providerMap = []; @@ -295,7 +345,17 @@ public static function getOfficeListByServiceId(int $serviceId, bool $showUnpubl return $errors; } - return new OfficeList($offices); + $result = new OfficeList($offices); + + if (\App::$cache) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + + return $result; } public static function getScopeById(?int $scopeId): ThinnedScope|array @@ -348,6 +408,12 @@ public static function getScopeById(?int $scopeId): ThinnedScope|array public static function getServicesByOfficeId(int $officeId, bool $showUnpublished = false): ServiceList|array { + $cacheKey = self::CACHE_KEY_SERVICES_BY_OFFICE_PREFIX . $officeId . ($showUnpublished ? '_unpublished' : ''); + + if (\App::$cache && ($cachedData = \App::$cache->get($cacheKey))) { + return $cachedData; + } + $requestList = ZmsApiClientService::getServices() ?? new RequestList(); $requestRelationList = ZmsApiClientService::getRequestRelationList() ?? new RequestRelationList(); $requestMap = []; @@ -380,7 +446,17 @@ public static function getServicesByOfficeId(int $officeId, bool $showUnpublishe return $errors; } - return new ServiceList($services); + $result = new ServiceList($services); + + if (\App::$cache) { + \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + + return $result; } public static function getServicesProvidedAtOffice(int $officeId): RequestList|array From f9376032460288fbaffd3a75f07e2c1e8e1979a9 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Thu, 27 Feb 2025 18:50:06 +0100 Subject: [PATCH 5/6] feat(ZMSKVR-138): refactor complexity for mapped cache --- .../Services/Core/ZmsApiFacadeService.php | 59 ++++++------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php index 06c925943..10aaaf740 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php @@ -52,6 +52,17 @@ private static function getError(string $key): array return ErrorMessages::get($key, self::$currentLanguage); } + private static function setMappedCache(string $cacheKey, mixed $data): void + { + if (\App::$cache) { + \App::$cache->set($cacheKey, $data, \App::$SOURCE_CACHE_TTL); + LoggerService::logInfo('Second-level cache set', [ + 'key' => $cacheKey, + 'ttl' => \App::$SOURCE_CACHE_TTL + ]); + } + } + public static function getOffices(bool $showUnpublished = false): OfficeList { $cacheKey = self::CACHE_KEY_OFFICES . ($showUnpublished ? '_unpublished' : ''); @@ -105,13 +116,7 @@ public static function getOffices(bool $showUnpublished = false): OfficeList $result = new OfficeList($offices); - if (\App::$cache) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Second-level cache set', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL - ]); - } + self::setMappedCache($cacheKey, $result); return $result; } @@ -159,13 +164,7 @@ public static function getScopes(): ThinnedScopeList|array $result = new ThinnedScopeList($scopesProjectionList); - if (\App::$cache) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Second-level cache set', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL - ]); - } + self::setMappedCache($cacheKey, $result); return $result; } @@ -195,13 +194,7 @@ public static function getServices(bool $showUnpublished = false): ServiceList|a $result = new ServiceList($services); - if (\App::$cache) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Second-level cache set', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL - ]); - } + self::setMappedCache($cacheKey, $result); return $result; } @@ -228,13 +221,7 @@ public static function getServicesAndOffices(bool $showUnpublished = false): Off $result = new OfficeServiceAndRelationList($offices, $services, $relations); - if (\App::$cache) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Second-level cache set', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL - ]); - } + self::setMappedCache($cacheKey, $result); return $result; } @@ -347,13 +334,7 @@ public static function getOfficeListByServiceId(int $serviceId, bool $showUnpubl $result = new OfficeList($offices); - if (\App::$cache) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Second-level cache set', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL - ]); - } + self::setMappedCache($cacheKey, $result); return $result; } @@ -448,13 +429,7 @@ public static function getServicesByOfficeId(int $officeId, bool $showUnpublishe $result = new ServiceList($services); - if (\App::$cache) { - \App::$cache->set($cacheKey, $result, \App::$SOURCE_CACHE_TTL); - LoggerService::logInfo('Second-level cache set', [ - 'key' => $cacheKey, - 'ttl' => \App::$SOURCE_CACHE_TTL - ]); - } + self::setMappedCache($cacheKey, $result); return $result; } From c05ffd47ac236ddee46dd7e129171c3d9f1816be Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Thu, 27 Feb 2025 19:05:55 +0100 Subject: [PATCH 6/6] feat(ZMSKVR-138): add formatting php todo --- .../src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php index 10aaaf740..5b495a24a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/Core/ZmsApiFacadeService.php @@ -289,6 +289,10 @@ public static function getScopeByOfficeId(int $officeId): ThinnedScope|array * */ + /** + * @SuppressWarnings(PHPMD.NPathComplexity) + * @TODO: Extract providerMap mapping logic into MapperService + */ public static function getOfficeListByServiceId(int $serviceId, bool $showUnpublished = false): OfficeList|array { $cacheKey = self::CACHE_KEY_OFFICES_BY_SERVICE_PREFIX . $serviceId . ($showUnpublished ? '_unpublished' : '');