diff --git a/CHANGELOG.md b/CHANGELOG.md index 404b5f0b..796a107d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file. - Extended OrgStructureCache TTL from 10 to 30 minutes (reduce cleanup frequency) - Added OrgStructureCache.destroy() in graceful shutdown handler +- Changed LogMemoryStore from active refresh (setInterval) to passive refresh on-access (60 min TTL) ### ⚙️ Miscellaneous Tasks diff --git a/docs/SUMMARY_OPTIMIZATION-fix-optimization.md b/docs/SUMMARY_OPTIMIZATION-fix-optimization.md index 6d4907c7..d2be25e1 100644 --- a/docs/SUMMARY_OPTIMIZATION-fix-optimization.md +++ b/docs/SUMMARY_OPTIMIZATION-fix-optimization.md @@ -104,15 +104,17 @@ class LogMemoryStore { profileCache: Map, // keycloak → Profile rootIdCache: Map, // profileId → rootId }; - private readonly REFRESH_INTERVAL = 10 * 60 * 1000; // 10 นาที + private readonly CACHE_TTL = 60 * 60 * 1000; // 60 นาที } ``` **การทำงาน:** -- Cache `currentRevision` ทุก 10 นาที +- Passive cache refresh - ตรวจสอบและ refresh cache เมื่อมีการเข้าถึงข้อมูล (on-access) +- หาก cache เก่าเกิน 60 นาที จะทำการ refresh อัตโนมัติ - Lazy load `profileCache` และ `rootIdCache` (โหลดเมื่อถูกเรียกใช้) - Method `getProfileByKeycloak()` - ดึง profile จาก cache หรือ database - Method `getRootIdByProfileId()` - ดึง rootId จาก cache หรือ database +- ไม่มี setInterval (ลดการใช้งาน timer) #### 3.2 Log Middleware (`src/middlewares/logs.ts`) diff --git a/src/utils/LogMemoryStore.ts b/src/utils/LogMemoryStore.ts index 456c06c8..24e84f9e 100644 --- a/src/utils/LogMemoryStore.ts +++ b/src/utils/LogMemoryStore.ts @@ -17,10 +17,8 @@ class LogMemoryStore { rootIdCache: new Map(), updatedAt: new Date(), }; - private readonly REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes - private isRefreshing = false; + private readonly CACHE_TTL = 60 * 60 * 1000; // 60 minutes private isInitialized = false; - private refreshTimer: NodeJS.Timeout | null = null; constructor() { // ไม่ refresh ทันที - รอให้เรียก initialize() หลัง TypeORM ready @@ -35,24 +33,15 @@ class LogMemoryStore { this.isInitialized = true; this.refreshCache(); - this.refreshTimer = setInterval(() => { - this.refreshCache(); - }, this.REFRESH_INTERVAL); console.log( "[LogMemoryStore] Initialized with", - this.REFRESH_INTERVAL / 1000, - "second refresh interval", + this.CACHE_TTL / 1000 / 60, + "minute TTL (passive cleanup on access)", ); } private async refreshCache() { - if (this.isRefreshing) { - console.log("[LogMemoryStore] Already refreshing, skipping..."); - return; - } - - this.isRefreshing = true; try { // Refresh revision cache const repoRevision = AppDataSource.getRepository(OrgRevision); @@ -72,12 +61,26 @@ class LogMemoryStore { console.log("[LogMemoryStore] Cache refreshed at", this.cache.updatedAt.toISOString()); } catch (error) { console.error("[LogMemoryStore] Error refreshing cache:", error); - } finally { - this.isRefreshing = false; + } + } + + // Check if cache is stale and refresh if needed + private async checkAndRefreshIfNeeded() { + const now = new Date(); + const age = now.getTime() - this.cache.updatedAt.getTime(); + if (age > this.CACHE_TTL) { + console.log( + "[LogMemoryStore] Cache is stale (age:", + Math.round(age / 1000 / 60), + "minutes), refreshing...", + ); + await this.refreshCache(); } } getCurrentRevision(): OrgRevision | null { + // Check for stale data (fire and forget) + this.checkAndRefreshIfNeeded(); return this.cache.currentRevision; } @@ -89,6 +92,9 @@ class LogMemoryStore { * Get Profile by keycloak ID with caching */ async getProfileByKeycloak(keycloak: string): Promise { + // Check for stale data + await this.checkAndRefreshIfNeeded(); + // Check cache first if (this.cache.profileCache.has(keycloak)) { return this.cache.profileCache.get(keycloak)!; @@ -114,6 +120,9 @@ class LogMemoryStore { async getRootIdByProfileId(profileId: string | undefined): Promise { if (!profileId) return null; + // Check for stale data + await this.checkAndRefreshIfNeeded(); + // Check cache first if (this.cache.rootIdCache.has(profileId)) { return this.cache.rootIdCache.get(profileId)!; @@ -148,10 +157,8 @@ class LogMemoryStore { // สำหรับ shutdown destroy() { - if (this.refreshTimer) { - clearInterval(this.refreshTimer); - this.refreshTimer = null; - } + // No active timer to clear + console.log("[LogMemoryStore] Destroyed"); } }