diff --git a/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-cache.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-cache.service.ts index 2c98b7288a..8b0f09b575 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-cache.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-cache.service.ts @@ -2,27 +2,24 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { type APP_LOCALES } from 'twenty-shared/translations'; -import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import { ApplicationTranslationEntity } from 'src/engine/core-modules/application/application-translation/application-translation.entity'; +import { PromiseMemoizer } from 'src/engine/twenty-orm/storage/promise-memoizer.storage'; +import { type CacheKey } from 'src/engine/twenty-orm/storage/types/cache-key.type'; type ApplicationCatalogsByLocale = Partial< Record> >; -type ApplicationTranslationCacheEntry = { - catalogsByLocale: ApplicationCatalogsByLocale; - loadedAt: number; -}; - const CACHE_TTL_MS = 30_000; const EMPTY_CATALOG: Record = {}; @Injectable() export class ApplicationTranslationCacheService { - private readonly cache = new Map(); + private readonly catalogsMemoizer = + new PromiseMemoizer(CACHE_TTL_MS); constructor( // applicationTranslation is a core cross-workspace table keyed by applicationRegistrationId, not workspaceId. @@ -38,27 +35,28 @@ export class ApplicationTranslationCacheService { applicationRegistrationId: string; locale: keyof typeof APP_LOCALES; }): Promise> { - const entry = await this.getOrLoadEntry(applicationRegistrationId); + const catalogsByLocale = + await this.catalogsMemoizer.memoizePromiseAndExecute( + this.getCacheKey(applicationRegistrationId), + () => this.loadCatalogsByLocale(applicationRegistrationId), + ); - return entry.catalogsByLocale[locale] ?? EMPTY_CATALOG; + return catalogsByLocale?.[locale] ?? EMPTY_CATALOG; } - invalidate(applicationRegistrationId: string): void { - this.cache.delete(applicationRegistrationId); + async invalidate(applicationRegistrationId: string): Promise { + await this.catalogsMemoizer.clearKeys( + this.getCacheKey(applicationRegistrationId), + ); } - private async getOrLoadEntry( + private getCacheKey(applicationRegistrationId: string): CacheKey { + return `applicationTranslation-${applicationRegistrationId}`; + } + + private async loadCatalogsByLocale( applicationRegistrationId: string, - ): Promise { - const cachedEntry = this.cache.get(applicationRegistrationId); - - if ( - isDefined(cachedEntry) && - Date.now() - cachedEntry.loadedAt < CACHE_TTL_MS - ) { - return cachedEntry; - } - + ): Promise { const rows = await this.applicationTranslationRepository.find({ where: { applicationRegistrationId }, }); @@ -69,13 +67,6 @@ export class ApplicationTranslationCacheService { catalogsByLocale[row.locale] = row.messages; } - const entry: ApplicationTranslationCacheEntry = { - catalogsByLocale, - loadedAt: Date.now(), - }; - - this.cache.set(applicationRegistrationId, entry); - - return entry; + return catalogsByLocale; } } diff --git a/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-sync.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-sync.service.ts index 9fdaa8b7d7..5d6f7d7a0f 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-sync.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-translation/application-translation-sync.service.ts @@ -89,7 +89,7 @@ export class ApplicationTranslationSyncService { ); } - this.applicationTranslationCacheService.invalidate( + await this.applicationTranslationCacheService.invalidate( applicationRegistrationId, ); }