diff --git a/services/iam-service/src/core/cache/multi-layer-cache.ts b/services/iam-service/src/core/cache/multi-layer-cache.ts index 133ac426..66e03768 100644 --- a/services/iam-service/src/core/cache/multi-layer-cache.ts +++ b/services/iam-service/src/core/cache/multi-layer-cache.ts @@ -9,7 +9,7 @@ import { getRedisClient } from '../../config/redis.config'; */ export class MultiLayerCache { private l1Cache: NodeCache; // In-memory cache - private redis: ReturnType; + private redis: ReturnType | null = null; // Disabled for testing constructor() { // L1: In-memory cache (10MB default, 60s default TTL) @@ -19,8 +19,10 @@ export class MultiLayerCache { useClones: false, // Better performance }); - // L2: Redis cache - this.redis = getRedisClient(); + // EN: Temporarily disable L2 Redis cache to test performance + // VI: Tạm thời tắt L2 Redis cache để test hiệu suất + // TODO: Re-enable after fixing Redis EPIPE errors + // this.redis = getRedisClient(); } /** @@ -35,13 +37,15 @@ export class MultiLayerCache { return l1Value; } - // L2: Check Redis cache (< 5ms) - const l2Value = await this.redis.get(key); - if (l2Value) { - const parsed = JSON.parse(l2Value) as T; - // Warm up L1 cache - this.l1Cache.set(key, parsed, 60); // Cache 1 minute in L1 - return parsed; + // L2: Check Redis cache (< 5ms) - only if Redis is enabled + if (this.redis) { + const l2Value = await this.redis.get(key); + if (l2Value) { + const parsed = JSON.parse(l2Value) as T; + // Warm up L1 cache + this.l1Cache.set(key, parsed, 60); // Cache 1 minute in L1 + return parsed; + } } return null; @@ -66,11 +70,13 @@ export class MultiLayerCache { // TODO: Re-enable after fixing Redis EPIPE errors /* // L2: Set Redis cache - const stringValue = JSON.stringify(value); - if (ttlSeconds) { - await this.redis.setex(key, ttlSeconds, stringValue); - } else { - await this.redis.set(key, stringValue); + if (this.redis) { + const stringValue = JSON.stringify(value); + if (ttlSeconds) { + await this.redis.setex(key, ttlSeconds, stringValue); + } else { + await this.redis.set(key, stringValue); + } } */ } catch (error) { @@ -108,7 +114,9 @@ export class MultiLayerCache { this.l1Cache.del(key); // L2: Delete from Redis - await this.redis.del(key); + if (this.redis) { + await this.redis.del(key); + } } catch (error) { logger.error('Multi-layer cache del error', { key, error }); } @@ -124,7 +132,7 @@ export class MultiLayerCache { keys.forEach(key => this.l1Cache.del(key)); // L2: Delete from Redis - if (keys.length > 0) { + if (this.redis && keys.length > 0) { await this.redis.del(...keys); } } catch (error) { @@ -143,21 +151,24 @@ export class MultiLayerCache { this.l1Cache.flushAll(); // L2: Scan and delete matching keys in Redis - const stream = this.redis.scanStream({ - match: pattern, - count: 100, - }); + if (this.redis) { + const stream = this.redis.scanStream({ + match: pattern, + count: 100, + }); - const keys: string[] = []; - stream.on('data', (resultKeys: string[]) => { - keys.push(...resultKeys); - }); + const keys: string[] = []; + stream.on('data', (resultKeys: string[]) => { + keys.push(...resultKeys); + }); - stream.on('end', async () => { - if (keys.length > 0) { - await this.redis.del(...keys); - } - }); + const redis = this.redis; // Capture for closure + stream.on('end', async () => { + if (keys.length > 0) { + await redis.del(...keys); + } + }); + } } catch (error) { logger.error('Multi-layer cache invalidatePattern error', { pattern, error }); } diff --git a/services/iam-service/src/modules/common/cache.service.ts b/services/iam-service/src/modules/common/cache.service.ts index bdddf34b..f9f9dbe5 100644 --- a/services/iam-service/src/modules/common/cache.service.ts +++ b/services/iam-service/src/modules/common/cache.service.ts @@ -1,21 +1,41 @@ import { logger } from '@goodgo/logger'; -import { getRedisClient } from '../../config/redis.config'; +// EN: Temporarily disable Redis to prevent EPIPE errors +// VI: Tạm thời tắt Redis để tránh lỗi EPIPE +// import { getRedisClient } from '../../config/redis.config'; /** - * EN: Service for caching data (Redis wrapper) - * VI: Service cho việc caching dữ liệu (Redis wrapper) + * EN: Service for caching data (In-memory fallback, Redis disabled) + * VI: Service cho việc caching dữ liệu (In-memory fallback, Redis đã tắt) */ export class CacheService { + // EN: Temporary in-memory cache as fallback + // VI: Cache in-memory tạm thời làm fallback + private memoryCache: Map = new Map(); + /** * EN: Get value from cache * VI: Lấy giá trị từ cache */ async get(key: string): Promise { try { - const data = await getRedisClient().get(key); - if (!data) return null; - return JSON.parse(data) as T; + // EN: Use in-memory cache instead of Redis + // VI: Sử dụng in-memory cache thay vì Redis + const cached = this.memoryCache.get(key); + if (!cached) return null; + + // Check expiration + if (cached.expiresAt < Date.now()) { + this.memoryCache.delete(key); + return null; + } + + return cached.value as T; + + // TODO: Re-enable after fixing Redis EPIPE errors + // const data = await getRedisClient().get(key); + // if (!data) return null; + // return JSON.parse(data) as T; } catch (error) { logger.error('Cache get error', { key, error }); return null; @@ -26,14 +46,20 @@ export class CacheService { * EN: Set value in cache * VI: Lưu giá trị vào cache */ - async set(key: string, value: any, ttlSeconds?: number): Promise { + async set(key: string, value: any, ttlSeconds: number = 300): Promise { try { - const stringValue = JSON.stringify(value); - if (ttlSeconds) { - await getRedisClient().setex(key, ttlSeconds, stringValue); - } else { - await getRedisClient().set(key, stringValue); - } + // EN: Use in-memory cache instead of Redis + // VI: Sử dụng in-memory cache thay vì Redis + const expiresAt = Date.now() + (ttlSeconds * 1000); + this.memoryCache.set(key, { value, expiresAt }); + + // TODO: Re-enable after fixing Redis EPIPE errors + // const stringValue = JSON.stringify(value); + // if (ttlSeconds) { + // await getRedisClient().setex(key, ttlSeconds, stringValue); + // } else { + // await getRedisClient().set(key, stringValue); + // } } catch (error) { logger.error('Cache set error', { key, error }); } @@ -62,7 +88,12 @@ export class CacheService { */ async del(key: string): Promise { try { - await getRedisClient().del(key); + // EN: Use in-memory cache instead of Redis + // VI: Sử dụng in-memory cache thay vì Redis + this.memoryCache.delete(key); + + // TODO: Re-enable after fixing Redis EPIPE errors + // await getRedisClient().del(key); } catch (error) { logger.error('Cache del error', { key, error }); }