Implement stale entry cleanup in FreebitOperationsService to prevent memory leaks

- Added a method to periodically clean up stale operation timestamps, ensuring efficient memory usage.
- Introduced rate limiting for cleanup operations to minimize performance overhead.
- Enhanced the getOperationWindow method to invoke cleanup, maintaining optimal state of operation timestamps.
- Included validation to ensure at least one feature is specified during updates, improving error handling.
This commit is contained in:
barsa 2025-11-26 16:58:49 +09:00
parent b7f6c204e2
commit e228f9342f

View File

@ -55,7 +55,51 @@ export class FreebitOperationsService {
}
>();
// Rate limit cleanup to avoid running on every operation
private lastCleanupTime = 0;
private readonly cleanupIntervalMs = 5 * 60 * 1000; // Run cleanup at most every 5 minutes
private readonly staleThresholdMs = 35 * 60 * 1000; // Remove entries older than 35 minutes (beyond 30-min window)
/**
* Cleanup stale entries from operationTimestamps to prevent memory leak.
* Entries are considered stale when all their timestamps are older than staleThresholdMs.
*/
private cleanupStaleEntries(): void {
const now = Date.now();
// Rate limit cleanup to avoid performance overhead
if (now - this.lastCleanupTime < this.cleanupIntervalMs) {
return;
}
this.lastCleanupTime = now;
const staleThreshold = now - this.staleThresholdMs;
const accountsToRemove: string[] = [];
for (const [account, entry] of this.operationTimestamps) {
const timestamps = [entry.voice, entry.network, entry.plan, entry.cancellation].filter(
(t): t is number => typeof t === "number"
);
// If all timestamps are stale (or entry is empty), mark for removal
if (timestamps.length === 0 || timestamps.every(t => t < staleThreshold)) {
accountsToRemove.push(account);
}
}
for (const account of accountsToRemove) {
this.operationTimestamps.delete(account);
}
if (accountsToRemove.length > 0) {
this.logger.debug(`Cleaned up ${accountsToRemove.length} stale operation timestamp entries`);
}
}
private getOperationWindow(account: string) {
// Run cleanup periodically to prevent memory leak
this.cleanupStaleEntries();
if (!this.operationTimestamps.has(account)) {
this.operationTimestamps.set(account, {});
}
@ -457,6 +501,14 @@ export class FreebitOperationsService {
);
}
// Validate that at least one feature is specified
if (!hasVoiceFeatures && !hasNetworkTypeChange) {
throw new BadRequestException(
"No features specified for update. Please provide at least one of: " +
"voiceMailEnabled, callWaitingEnabled, internationalRoamingEnabled, or networkType."
);
}
if (hasVoiceFeatures) {
// Only voice features (PA05-06)
await this.updateVoiceFeatures(account, voiceFeatures);