hrms-api-org/src/utils/OrgStructureCache.ts
waruneeauy 7955c855bc fix: extend OrgStructureCache TTL and add graceful shutdown cleanup
- Extended OrgStructureCache TTL from 10 to 30 minutes (reduce cleanup frequency)
- Added orgStructureCache.destroy() in graceful shutdown handler
- Updated documentation to reflect changes

Co-Authored-By: Claude (glm-4.7) <noreply@anthropic.com>
2026-01-29 00:05:56 +07:00

96 lines
2.4 KiB
TypeScript

interface CacheEntry {
data: any;
cachedAt: Date;
}
class OrgStructureCache {
private cache: Map<string, CacheEntry> = new Map();
private readonly CACHE_TTL = 30 * 60 * 1000; // 30 minutes
private isInitialized = false;
private cleanupTimer: NodeJS.Timeout | null = null;
constructor() {
// Don't auto-initialize - wait for AppDataSource to be ready
}
initialize() {
if (this.isInitialized) return;
this.isInitialized = true;
// Cleanup expired entries every 30 minutes
this.cleanupTimer = setInterval(() => {
this.cleanup();
}, this.CACHE_TTL);
console.log("[OrgStructureCache] Initialized");
}
private generateKey(revisionId: string, rootId?: string): string {
return `org-structure-${revisionId}-${rootId || "all"}`;
}
private cleanup() {
const now = Date.now();
let cleaned = 0;
for (const [key, entry] of this.cache.entries()) {
const age = now - entry.cachedAt.getTime();
if (age > this.CACHE_TTL) {
this.cache.delete(key);
cleaned++;
}
}
if (cleaned > 0) {
console.log(`[OrgStructureCache] Cleaned ${cleaned} expired entries`);
}
}
async get(revisionId: string, rootId?: string): Promise<any | null> {
const key = this.generateKey(revisionId, rootId);
const entry = this.cache.get(key);
if (!entry) {
return null;
}
// Check if expired
const age = Date.now() - entry.cachedAt.getTime();
if (age > this.CACHE_TTL) {
this.cache.delete(key);
return null;
}
console.log(`[OrgStructureCache] HIT: ${key}`);
return entry.data;
}
async set(revisionId: string, rootId: string | undefined, data: any): Promise<void> {
const key = this.generateKey(revisionId, rootId);
this.cache.set(key, {
data,
cachedAt: new Date(),
});
console.log(`[OrgStructureCache] SET: ${key}`);
}
invalidate(revisionId: string): void {
// Invalidate all entries for this revision
for (const key of this.cache.keys()) {
if (key.startsWith(`org-structure-${revisionId}`)) {
this.cache.delete(key);
}
}
console.log(`[OrgStructureCache] INVALIDATED: ${revisionId}`);
}
destroy() {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
this.cache.clear();
}
}
export const orgStructureCache = new OrgStructureCache();