diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/__tests__/promise-memoizer.storage.spec.ts b/packages/twenty-server/src/engine/twenty-orm/storage/__tests__/promise-memoizer.storage.spec.ts index a151b3e735..c8f2fadb15 100644 --- a/packages/twenty-server/src/engine/twenty-orm/storage/__tests__/promise-memoizer.storage.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/storage/__tests__/promise-memoizer.storage.spec.ts @@ -83,6 +83,29 @@ describe('PromiseMemoizer', () => { expect(mockFactory).toHaveBeenCalledTimes(2); }); + it('should re-execute factory after TTL expires even under continuous reads', async () => { + mockFactory.mockResolvedValue('test-value'); + + const startTime = Date.now(); + + await memoizer.memoizePromiseAndExecute('test-key-1', mockFactory); + + for (const elapsedTime of [ + TTL_MS / 2, + TTL_MS, + (3 * TTL_MS) / 2, + 2 * TTL_MS, + ]) { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => startTime + elapsedTime + 1); + + await memoizer.memoizePromiseAndExecute('test-key-1', mockFactory); + } + + expect(mockFactory).toHaveBeenCalledTimes(3); + }); + it('should handle null values', async () => { mockFactory.mockResolvedValue(null); diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts b/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts index f0533b4929..b8171e755c 100644 --- a/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts +++ b/packages/twenty-server/src/engine/twenty-orm/storage/promise-memoizer.storage.ts @@ -8,7 +8,7 @@ type AsyncFactoryCallback = () => Promise; const ONE_HOUR_IN_MS = 3600_000; export class PromiseMemoizer { - private cache = new Map(); + private cache = new Map(); private pending = new Map>(); private ttlMs: number; @@ -26,8 +26,6 @@ export class PromiseMemoizer { const cachedEntry = this.cache.get(cacheKey); if (cachedEntry) { - cachedEntry.lastUsed = Date.now(); - return cachedEntry.value; } @@ -42,7 +40,10 @@ export class PromiseMemoizer { const value = await factory(); if (value) { - this.cache.set(cacheKey, { value, lastUsed: Date.now() }); + this.cache.set(cacheKey, { + value, + expiresAt: Date.now() + this.ttlMs, + }); } return value; @@ -60,7 +61,7 @@ export class PromiseMemoizer { const now = Date.now(); for (const [cacheKey, cachedEntry] of this.cache.entries()) { - if (cachedEntry.lastUsed < now - this.ttlMs) { + if (cachedEntry.expiresAt <= now) { await this.clearKey(cacheKey, onDelete); } }