feat: Vô hiệu hóa tạm thời Redis cache và sử dụng in-memory cache làm fallback.
This commit is contained in:
@@ -9,7 +9,7 @@ import { getRedisClient } from '../../config/redis.config';
|
||||
*/
|
||||
export class MultiLayerCache {
|
||||
private l1Cache: NodeCache; // In-memory cache
|
||||
private redis: ReturnType<typeof getRedisClient>;
|
||||
private redis: ReturnType<typeof getRedisClient> | 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 });
|
||||
}
|
||||
|
||||
@@ -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<string, { value: any; expiresAt: number }> = new Map();
|
||||
|
||||
/**
|
||||
* EN: Get value from cache
|
||||
* VI: Lấy giá trị từ cache
|
||||
*/
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
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<void> {
|
||||
async set(key: string, value: any, ttlSeconds: number = 300): Promise<void> {
|
||||
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<void> {
|
||||
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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user