running prettier for cleanup
This commit is contained in:
parent
fe7733aeb5
commit
d85cd58acd
151 changed files with 29158 additions and 27966 deletions
109
libs/cache/src/redis-cache.ts
vendored
109
libs/cache/src/redis-cache.ts
vendored
|
|
@ -1,7 +1,7 @@
|
|||
import Redis from 'ioredis';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import { CacheProvider, CacheOptions, CacheStats } from './types';
|
||||
import { RedisConnectionManager } from './connection-manager';
|
||||
import { CacheOptions, CacheProvider, CacheStats } from './types';
|
||||
|
||||
/**
|
||||
* Simplified Redis-based cache provider using connection manager
|
||||
|
|
@ -15,27 +15,33 @@ export class RedisCache implements CacheProvider {
|
|||
private isConnected = false;
|
||||
private startTime = Date.now();
|
||||
private connectionManager: RedisConnectionManager;
|
||||
|
||||
|
||||
private stats: CacheStats = {
|
||||
hits: 0,
|
||||
misses: 0,
|
||||
errors: 0,
|
||||
hitRate: 0,
|
||||
total: 0,
|
||||
uptime: 0
|
||||
uptime: 0,
|
||||
};
|
||||
|
||||
constructor(options: CacheOptions = {}) {
|
||||
this.defaultTTL = options.ttl ?? 3600; // 1 hour default
|
||||
this.keyPrefix = options.keyPrefix ?? 'cache:';
|
||||
this.enableMetrics = options.enableMetrics ?? true;
|
||||
|
||||
|
||||
// Get connection manager instance
|
||||
this.connectionManager = RedisConnectionManager.getInstance();
|
||||
|
||||
|
||||
// Generate connection name based on cache type
|
||||
const baseName = options.name || this.keyPrefix.replace(':', '').replace(/[^a-zA-Z0-9]/g, '').toUpperCase() || 'CACHE';
|
||||
|
||||
const baseName =
|
||||
options.name ||
|
||||
this.keyPrefix
|
||||
.replace(':', '')
|
||||
.replace(/[^a-zA-Z0-9]/g, '')
|
||||
.toUpperCase() ||
|
||||
'CACHE';
|
||||
|
||||
// Get Redis connection (shared by default for cache)
|
||||
this.redis = this.connectionManager.getConnection({
|
||||
name: `${baseName}-SERVICE`,
|
||||
|
|
@ -110,7 +116,7 @@ export class RedisCache implements CacheProvider {
|
|||
return await operation();
|
||||
} catch (error) {
|
||||
this.logger.error(`Redis ${operationName} failed`, {
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
this.updateStats(false, true);
|
||||
return fallback;
|
||||
|
|
@ -122,7 +128,7 @@ export class RedisCache implements CacheProvider {
|
|||
async () => {
|
||||
const fullKey = this.getKey(key);
|
||||
const value = await this.redis.get(fullKey);
|
||||
|
||||
|
||||
if (value === null) {
|
||||
this.updateStats(false);
|
||||
this.logger.debug('Cache miss', { key });
|
||||
|
|
@ -131,7 +137,7 @@ export class RedisCache implements CacheProvider {
|
|||
|
||||
this.updateStats(true);
|
||||
this.logger.debug('Cache hit', { key });
|
||||
|
||||
|
||||
try {
|
||||
return JSON.parse(value) as T;
|
||||
} catch {
|
||||
|
|
@ -144,23 +150,29 @@ export class RedisCache implements CacheProvider {
|
|||
);
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T, options?: number | {
|
||||
ttl?: number;
|
||||
preserveTTL?: boolean;
|
||||
onlyIfExists?: boolean;
|
||||
onlyIfNotExists?: boolean;
|
||||
getOldValue?: boolean;
|
||||
}): Promise<T | null> {
|
||||
async set<T>(
|
||||
key: string,
|
||||
value: T,
|
||||
options?:
|
||||
| number
|
||||
| {
|
||||
ttl?: number;
|
||||
preserveTTL?: boolean;
|
||||
onlyIfExists?: boolean;
|
||||
onlyIfNotExists?: boolean;
|
||||
getOldValue?: boolean;
|
||||
}
|
||||
): Promise<T | null> {
|
||||
return this.safeExecute(
|
||||
async () => {
|
||||
const fullKey = this.getKey(key);
|
||||
const serialized = typeof value === 'string' ? value : JSON.stringify(value);
|
||||
|
||||
|
||||
// Handle backward compatibility - if options is a number, treat as TTL
|
||||
const config = typeof options === 'number' ? { ttl: options } : (options || {});
|
||||
|
||||
const config = typeof options === 'number' ? { ttl: options } : options || {};
|
||||
|
||||
let oldValue: T | null = null;
|
||||
|
||||
|
||||
// Get old value if requested
|
||||
if (config.getOldValue) {
|
||||
const existingValue = await this.redis.get(fullKey);
|
||||
|
|
@ -172,15 +184,17 @@ export class RedisCache implements CacheProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle preserveTTL logic
|
||||
if (config.preserveTTL) {
|
||||
const currentTTL = await this.redis.ttl(fullKey);
|
||||
|
||||
|
||||
if (currentTTL === -2) {
|
||||
// Key doesn't exist
|
||||
if (config.onlyIfExists) {
|
||||
this.logger.debug('Set skipped - key does not exist and onlyIfExists is true', { key });
|
||||
this.logger.debug('Set skipped - key does not exist and onlyIfExists is true', {
|
||||
key,
|
||||
});
|
||||
return oldValue;
|
||||
}
|
||||
// Set with default or specified TTL
|
||||
|
|
@ -201,7 +215,7 @@ export class RedisCache implements CacheProvider {
|
|||
if (config.onlyIfExists && config.onlyIfNotExists) {
|
||||
throw new Error('Cannot specify both onlyIfExists and onlyIfNotExists');
|
||||
}
|
||||
|
||||
|
||||
if (config.onlyIfExists) {
|
||||
// Only set if key exists (XX flag)
|
||||
const ttl = config.ttl ?? this.defaultTTL;
|
||||
|
|
@ -223,10 +237,10 @@ export class RedisCache implements CacheProvider {
|
|||
const ttl = config.ttl ?? this.defaultTTL;
|
||||
await this.redis.setex(fullKey, ttl, serialized);
|
||||
}
|
||||
|
||||
|
||||
this.logger.debug('Cache set', { key, ttl: config.ttl ?? this.defaultTTL });
|
||||
}
|
||||
|
||||
|
||||
return oldValue;
|
||||
},
|
||||
null,
|
||||
|
|
@ -278,8 +292,8 @@ export class RedisCache implements CacheProvider {
|
|||
const pong = await this.redis.ping();
|
||||
return pong === 'PONG';
|
||||
} catch (error) {
|
||||
this.logger.error('Redis health check failed', {
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
this.logger.error('Redis health check failed', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
|
@ -288,7 +302,7 @@ export class RedisCache implements CacheProvider {
|
|||
getStats(): CacheStats {
|
||||
return {
|
||||
...this.stats,
|
||||
uptime: Date.now() - this.startTime
|
||||
uptime: Date.now() - this.startTime,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +322,7 @@ export class RedisCache implements CacheProvider {
|
|||
resolve();
|
||||
});
|
||||
|
||||
this.redis.once('error', (error) => {
|
||||
this.redis.once('error', error => {
|
||||
clearTimeout(timeoutId);
|
||||
reject(error);
|
||||
});
|
||||
|
|
@ -318,12 +332,12 @@ export class RedisCache implements CacheProvider {
|
|||
isReady(): boolean {
|
||||
// Always check the actual Redis connection status
|
||||
const ready = this.redis.status === 'ready';
|
||||
|
||||
|
||||
// Update local flag if we're not using shared connection
|
||||
if (this.isConnected !== ready) {
|
||||
this.isConnected = ready;
|
||||
}
|
||||
|
||||
|
||||
return ready;
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +348,7 @@ export class RedisCache implements CacheProvider {
|
|||
|
||||
async setIfExists<T>(key: string, value: T, ttl?: number): Promise<boolean> {
|
||||
const result = await this.set(key, value, { ttl, onlyIfExists: true });
|
||||
return result !== null || await this.exists(key);
|
||||
return result !== null || (await this.exists(key));
|
||||
}
|
||||
|
||||
async setIfNotExists<T>(key: string, value: T, ttl?: number): Promise<boolean> {
|
||||
|
|
@ -347,11 +361,15 @@ export class RedisCache implements CacheProvider {
|
|||
}
|
||||
|
||||
// Atomic update with transformation
|
||||
async updateField<T>(key: string, updater: (current: T | null) => T, ttl?: number): Promise<T | null> {
|
||||
async updateField<T>(
|
||||
key: string,
|
||||
updater: (current: T | null) => T,
|
||||
ttl?: number
|
||||
): Promise<T | null> {
|
||||
return this.safeExecute(
|
||||
async () => {
|
||||
const fullKey = this.getKey(key);
|
||||
|
||||
|
||||
// Use Lua script for atomic read-modify-write
|
||||
const luaScript = `
|
||||
local key = KEYS[1]
|
||||
|
|
@ -363,13 +381,12 @@ export class RedisCache implements CacheProvider {
|
|||
-- Return current value for processing
|
||||
return {current_value, current_ttl}
|
||||
`;
|
||||
|
||||
const [currentValue, currentTTL] = await this.redis.eval(
|
||||
luaScript,
|
||||
1,
|
||||
fullKey
|
||||
) as [string | null, number];
|
||||
|
||||
|
||||
const [currentValue, currentTTL] = (await this.redis.eval(luaScript, 1, fullKey)) as [
|
||||
string | null,
|
||||
number,
|
||||
];
|
||||
|
||||
// Parse current value
|
||||
let parsed: T | null = null;
|
||||
if (currentValue !== null) {
|
||||
|
|
@ -379,10 +396,10 @@ export class RedisCache implements CacheProvider {
|
|||
parsed = currentValue as unknown as T;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Apply updater function
|
||||
const newValue = updater(parsed);
|
||||
|
||||
|
||||
// Set the new value with appropriate TTL logic
|
||||
if (ttl !== undefined) {
|
||||
// Use specified TTL
|
||||
|
|
@ -394,7 +411,7 @@ export class RedisCache implements CacheProvider {
|
|||
// Preserve existing TTL
|
||||
await this.set(key, newValue, { preserveTTL: true });
|
||||
}
|
||||
|
||||
|
||||
return parsed;
|
||||
},
|
||||
null,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue