fix: เพิ่ม Graceful Shutdown - ป้องกัน connection in app file, Log Mnddleware + Memory Store

This commit is contained in:
Warunee Tamkoo 2026-01-28 17:22:10 +07:00
parent a194d8594b
commit e068aafe3a
3 changed files with 137 additions and 33 deletions

View file

@ -1,17 +1,23 @@
import { AppDataSource } from "../database/data-source";
import { OrgRevision } from "../entities/OrgRevision";
import { Profile } from "../entities/Profile";
import { PosMaster } from "../entities/PosMaster";
interface LogCacheData {
currentRevision: OrgRevision | null;
profileCache: Map<string, Profile>; // keycloak → Profile
rootIdCache: Map<string, string>; // profileId → rootId
updatedAt: Date;
}
class LogMemoryStore {
private cache: LogCacheData = {
currentRevision: null,
profileCache: new Map(),
rootIdCache: new Map(),
updatedAt: new Date(),
};
private readonly REFRESH_INTERVAL = 5 * 60 * 1000; // 5 นาที
private readonly REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes
private isRefreshing = false;
private isInitialized = false;
private refreshTimer: NodeJS.Timeout | null = null;
@ -33,7 +39,11 @@ class LogMemoryStore {
this.refreshCache();
}, this.REFRESH_INTERVAL);
console.log("[LogMemoryStore] Initialized with", this.REFRESH_INTERVAL / 1000, "second refresh interval");
console.log(
"[LogMemoryStore] Initialized with",
this.REFRESH_INTERVAL / 1000,
"second refresh interval",
);
}
private async refreshCache() {
@ -44,6 +54,7 @@ class LogMemoryStore {
this.isRefreshing = true;
try {
// Refresh revision cache
const repoRevision = AppDataSource.getRepository(OrgRevision);
const revision = await repoRevision.findOne({
where: {
@ -52,11 +63,13 @@ class LogMemoryStore {
},
});
this.cache.currentRevision = revision;
// Clear on-demand caches (they will be rebuilt as needed)
this.cache.profileCache.clear();
this.cache.rootIdCache.clear();
this.cache.updatedAt = new Date();
console.log(
"[LogMemoryStore] Cache refreshed at",
this.cache.updatedAt.toISOString(),
);
console.log("[LogMemoryStore] Cache refreshed at", this.cache.updatedAt.toISOString());
} catch (error) {
console.error("[LogMemoryStore] Error refreshing cache:", error);
} finally {
@ -72,6 +85,67 @@ class LogMemoryStore {
return this.cache.updatedAt;
}
/**
* Get Profile by keycloak ID with caching
*/
async getProfileByKeycloak(keycloak: string): Promise<Profile | null> {
// Check cache first
if (this.cache.profileCache.has(keycloak)) {
return this.cache.profileCache.get(keycloak)!;
}
// Fetch from database
const repoProfile = AppDataSource.getRepository(Profile);
const profile = await repoProfile.findOne({
where: { keycloak },
});
// Cache the result
if (profile) {
this.cache.profileCache.set(keycloak, profile);
}
return profile;
}
/**
* Get RootId by profileId with caching
*/
async getRootIdByProfileId(profileId: string | undefined): Promise<string | null> {
if (!profileId) return null;
// Check cache first
if (this.cache.rootIdCache.has(profileId)) {
return this.cache.rootIdCache.get(profileId)!;
}
// Fetch from database
const repoPosmaster = AppDataSource.getRepository(PosMaster);
const revision = this.getCurrentRevision();
//
const posMaster = await repoPosmaster.findOne({
where: {
current_holderId: profileId,
orgRevisionId: revision?.id,
},
relations: ["orgRoot"],
select: {
orgRoot: {
ancestorDNA: true,
},
},
});
const rootId = posMaster?.orgRoot?.ancestorDNA ?? null;
// Cache the result
if (rootId) {
this.cache.rootIdCache.set(profileId, rootId);
}
return rootId;
}
// สำหรับ shutdown
destroy() {
if (this.refreshTimer) {