Changed LogMemoryStore from active refresh (setInterval) to passive refresh on-access (60 min TTL)
This commit is contained in:
parent
7955c855bc
commit
656c2e7341
3 changed files with 32 additions and 22 deletions
|
|
@ -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)
|
- Extended OrgStructureCache TTL from 10 to 30 minutes (reduce cleanup frequency)
|
||||||
- Added OrgStructureCache.destroy() in graceful shutdown handler
|
- Added OrgStructureCache.destroy() in graceful shutdown handler
|
||||||
|
- Changed LogMemoryStore from active refresh (setInterval) to passive refresh on-access (60 min TTL)
|
||||||
|
|
||||||
### ⚙️ Miscellaneous Tasks
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,15 +104,17 @@ class LogMemoryStore {
|
||||||
profileCache: Map<string, Profile>, // keycloak → Profile
|
profileCache: Map<string, Profile>, // keycloak → Profile
|
||||||
rootIdCache: Map<string, string>, // profileId → rootId
|
rootIdCache: Map<string, string>, // 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` (โหลดเมื่อถูกเรียกใช้)
|
- Lazy load `profileCache` และ `rootIdCache` (โหลดเมื่อถูกเรียกใช้)
|
||||||
- Method `getProfileByKeycloak()` - ดึง profile จาก cache หรือ database
|
- Method `getProfileByKeycloak()` - ดึง profile จาก cache หรือ database
|
||||||
- Method `getRootIdByProfileId()` - ดึง rootId จาก cache หรือ database
|
- Method `getRootIdByProfileId()` - ดึง rootId จาก cache หรือ database
|
||||||
|
- ไม่มี setInterval (ลดการใช้งาน timer)
|
||||||
|
|
||||||
#### 3.2 Log Middleware (`src/middlewares/logs.ts`)
|
#### 3.2 Log Middleware (`src/middlewares/logs.ts`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,8 @@ class LogMemoryStore {
|
||||||
rootIdCache: new Map(),
|
rootIdCache: new Map(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
};
|
};
|
||||||
private readonly REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
private readonly CACHE_TTL = 60 * 60 * 1000; // 60 minutes
|
||||||
private isRefreshing = false;
|
|
||||||
private isInitialized = false;
|
private isInitialized = false;
|
||||||
private refreshTimer: NodeJS.Timeout | null = null;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// ไม่ refresh ทันที - รอให้เรียก initialize() หลัง TypeORM ready
|
// ไม่ refresh ทันที - รอให้เรียก initialize() หลัง TypeORM ready
|
||||||
|
|
@ -35,24 +33,15 @@ class LogMemoryStore {
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
this.refreshCache();
|
this.refreshCache();
|
||||||
this.refreshTimer = setInterval(() => {
|
|
||||||
this.refreshCache();
|
|
||||||
}, this.REFRESH_INTERVAL);
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"[LogMemoryStore] Initialized with",
|
"[LogMemoryStore] Initialized with",
|
||||||
this.REFRESH_INTERVAL / 1000,
|
this.CACHE_TTL / 1000 / 60,
|
||||||
"second refresh interval",
|
"minute TTL (passive cleanup on access)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async refreshCache() {
|
private async refreshCache() {
|
||||||
if (this.isRefreshing) {
|
|
||||||
console.log("[LogMemoryStore] Already refreshing, skipping...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isRefreshing = true;
|
|
||||||
try {
|
try {
|
||||||
// Refresh revision cache
|
// Refresh revision cache
|
||||||
const repoRevision = AppDataSource.getRepository(OrgRevision);
|
const repoRevision = AppDataSource.getRepository(OrgRevision);
|
||||||
|
|
@ -72,12 +61,26 @@ class LogMemoryStore {
|
||||||
console.log("[LogMemoryStore] Cache refreshed at", this.cache.updatedAt.toISOString());
|
console.log("[LogMemoryStore] Cache refreshed at", this.cache.updatedAt.toISOString());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[LogMemoryStore] Error refreshing cache:", 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 {
|
getCurrentRevision(): OrgRevision | null {
|
||||||
|
// Check for stale data (fire and forget)
|
||||||
|
this.checkAndRefreshIfNeeded();
|
||||||
return this.cache.currentRevision;
|
return this.cache.currentRevision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +92,9 @@ class LogMemoryStore {
|
||||||
* Get Profile by keycloak ID with caching
|
* Get Profile by keycloak ID with caching
|
||||||
*/
|
*/
|
||||||
async getProfileByKeycloak(keycloak: string): Promise<Profile | null> {
|
async getProfileByKeycloak(keycloak: string): Promise<Profile | null> {
|
||||||
|
// Check for stale data
|
||||||
|
await this.checkAndRefreshIfNeeded();
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
if (this.cache.profileCache.has(keycloak)) {
|
if (this.cache.profileCache.has(keycloak)) {
|
||||||
return this.cache.profileCache.get(keycloak)!;
|
return this.cache.profileCache.get(keycloak)!;
|
||||||
|
|
@ -114,6 +120,9 @@ class LogMemoryStore {
|
||||||
async getRootIdByProfileId(profileId: string | undefined): Promise<string | null> {
|
async getRootIdByProfileId(profileId: string | undefined): Promise<string | null> {
|
||||||
if (!profileId) return null;
|
if (!profileId) return null;
|
||||||
|
|
||||||
|
// Check for stale data
|
||||||
|
await this.checkAndRefreshIfNeeded();
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
if (this.cache.rootIdCache.has(profileId)) {
|
if (this.cache.rootIdCache.has(profileId)) {
|
||||||
return this.cache.rootIdCache.get(profileId)!;
|
return this.cache.rootIdCache.get(profileId)!;
|
||||||
|
|
@ -148,10 +157,8 @@ class LogMemoryStore {
|
||||||
|
|
||||||
// สำหรับ shutdown
|
// สำหรับ shutdown
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.refreshTimer) {
|
// No active timer to clear
|
||||||
clearInterval(this.refreshTimer);
|
console.log("[LogMemoryStore] Destroyed");
|
||||||
this.refreshTimer = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue