From 1a9fd77599e951cad3700523cd5a9875c4cb8bdc Mon Sep 17 00:00:00 2001 From: fullex <0xfullex@gmail.com> Date: Wed, 19 Nov 2025 22:23:09 +0800 Subject: [PATCH] feat: implement garbage collection in CacheService and update cleanup interval in PreferenceService - Added a garbage collection mechanism in CacheService to automatically remove expired cache entries every 10 minutes. - Introduced a cleanup interval in PreferenceService, adjusting the frequency to every 5 minutes for better resource management. - Enhanced cleanup methods in both services to ensure proper resource release during shutdown. --- src/main/data/CacheService.ts | 39 ++++++++++++++++++++++++++++++ src/main/data/PreferenceService.ts | 24 ++++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/main/data/CacheService.ts b/src/main/data/CacheService.ts index d7571205d2..79e8104999 100644 --- a/src/main/data/CacheService.ts +++ b/src/main/data/CacheService.ts @@ -45,6 +45,10 @@ export class CacheService { // Main process cache private cache = new Map() + // GC timer reference and interval time (e.g., every 10 minutes) + private gcInterval: NodeJS.Timeout | null = null + private readonly GC_INTERVAL_MS = 10 * 60 * 1000 + private constructor() { // Private constructor for singleton pattern } @@ -56,6 +60,9 @@ export class CacheService { } this.setupIpcHandlers() + // Start garbage collection + this.startGarbageCollection() + logger.info('CacheService initialized') } @@ -71,6 +78,32 @@ export class CacheService { // ============ Main Process Cache (Internal) ============ + /** + * Garbage collection logic + */ + private startGarbageCollection() { + if (this.gcInterval) return + + this.gcInterval = setInterval(() => { + const now = Date.now() + let removedCount = 0 + + for (const [key, entry] of this.cache.entries()) { + if (entry.expireAt && now > entry.expireAt) { + this.cache.delete(key) + removedCount++ + } + } + + if (removedCount > 0) { + logger.debug(`Garbage collection removed ${removedCount} expired items`) + } + }, this.GC_INTERVAL_MS) + + // unref allows the process to exit if there are no other activities + this.gcInterval.unref() + } + /** * Get value from main process cache */ @@ -158,6 +191,12 @@ export class CacheService { * Cleanup resources */ public cleanup(): void { + // Clear the garbage collection interval + if (this.gcInterval) { + clearInterval(this.gcInterval) + this.gcInterval = null + } + // Clear cache this.cache.clear() diff --git a/src/main/data/PreferenceService.ts b/src/main/data/PreferenceService.ts index ca3d8af9a8..9f25964979 100644 --- a/src/main/data/PreferenceService.ts +++ b/src/main/data/PreferenceService.ts @@ -144,6 +144,9 @@ export class PreferenceService { // Custom notifier for main process change notifications private notifier = new PreferenceNotifier() + // Saves the reference to the cleanup interval + private cleanupInterval: NodeJS.Timeout | null = null + private constructor() { this.setupWindowCleanup() } @@ -501,8 +504,9 @@ export class PreferenceService { } } - // Run cleanup periodically (every 30 seconds) - setInterval(cleanup, 30000) + // Run cleanup periodically (every 5 minutes) + this.cleanupInterval = setInterval(cleanup, 300 * 1000) + this.cleanupInterval.unref() } /** @@ -525,6 +529,22 @@ export class PreferenceService { return new Map(this.subscriptions) } + /** + * Public cleanup method (for app shutdown or test teardown) + */ + public cleanup(): void { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval) + this.cleanupInterval = null + } + + this.notifier.removeAllSubscriptions() + this.subscriptions.clear() + this.initialized = false + + logger.debug('PreferenceService cleanup completed') + } + /** * Deep equality check for preference values * Handles primitives, arrays, and plain objects