169 lines
No EOL
4.4 KiB
TypeScript
169 lines
No EOL
4.4 KiB
TypeScript
import { createCache, type CacheProvider, type CacheStats } from '@stock-bot/cache';
|
|
import type { RedisConfig } from './types';
|
|
import { getServiceConfig } from './service-registry';
|
|
|
|
/**
|
|
* Service-aware cache that uses the service's Redis DB
|
|
* Automatically prefixes keys with the service's cache namespace
|
|
*/
|
|
export class ServiceCache implements CacheProvider {
|
|
private cache: CacheProvider;
|
|
private prefix: string;
|
|
|
|
constructor(
|
|
serviceName: string,
|
|
redisConfig: RedisConfig,
|
|
isGlobalCache: boolean = false,
|
|
logger?: any
|
|
) {
|
|
// Get service configuration
|
|
const serviceConfig = getServiceConfig(serviceName);
|
|
if (!serviceConfig && !isGlobalCache) {
|
|
throw new Error(`Unknown service: ${serviceName}`);
|
|
}
|
|
|
|
// Determine Redis DB and prefix
|
|
let db: number;
|
|
let prefix: string;
|
|
|
|
if (isGlobalCache) {
|
|
// Global cache uses db:0
|
|
db = 0;
|
|
prefix = 'stock-bot:shared';
|
|
} else {
|
|
// Service cache uses service's DB
|
|
db = serviceConfig!.db;
|
|
prefix = serviceConfig!.cachePrefix;
|
|
}
|
|
|
|
// Create underlying cache with correct DB
|
|
const cacheConfig = {
|
|
redisConfig: {
|
|
...redisConfig,
|
|
db,
|
|
},
|
|
keyPrefix: prefix + ':',
|
|
logger,
|
|
};
|
|
|
|
this.cache = createCache(cacheConfig);
|
|
this.prefix = prefix;
|
|
}
|
|
|
|
// Implement CacheProvider interface
|
|
async get<T = any>(key: string): Promise<T | null> {
|
|
return this.cache.get<T>(key);
|
|
}
|
|
|
|
async set<T = any>(
|
|
key: string,
|
|
value: T,
|
|
options?:
|
|
| number
|
|
| {
|
|
ttl?: number;
|
|
preserveTTL?: boolean;
|
|
onlyIfExists?: boolean;
|
|
onlyIfNotExists?: boolean;
|
|
getOldValue?: boolean;
|
|
}
|
|
): Promise<T | null> {
|
|
return this.cache.set(key, value, options);
|
|
}
|
|
|
|
async del(key: string): Promise<void> {
|
|
return this.cache.del(key);
|
|
}
|
|
|
|
async exists(key: string): Promise<boolean> {
|
|
return this.cache.exists(key);
|
|
}
|
|
|
|
async clear(): Promise<void> {
|
|
return this.cache.clear();
|
|
}
|
|
|
|
async keys(pattern: string): Promise<string[]> {
|
|
return this.cache.keys(pattern);
|
|
}
|
|
|
|
getStats(): CacheStats {
|
|
return this.cache.getStats();
|
|
}
|
|
|
|
async health(): Promise<boolean> {
|
|
return this.cache.health();
|
|
}
|
|
|
|
async waitForReady(timeout?: number): Promise<void> {
|
|
return this.cache.waitForReady(timeout);
|
|
}
|
|
|
|
isReady(): boolean {
|
|
return this.cache.isReady();
|
|
}
|
|
|
|
// Enhanced cache methods (delegate to underlying cache if available)
|
|
async update<T = any>(key: string, value: T): Promise<T | null> {
|
|
if (this.cache.update) {
|
|
return this.cache.update(key, value);
|
|
}
|
|
// Fallback implementation
|
|
return this.cache.set(key, value, { preserveTTL: true });
|
|
}
|
|
|
|
async setIfExists<T = any>(key: string, value: T, ttl?: number): Promise<boolean> {
|
|
if (this.cache.setIfExists) {
|
|
return this.cache.setIfExists(key, value, ttl);
|
|
}
|
|
// Fallback implementation
|
|
const result = await this.cache.set(key, value, { onlyIfExists: true, ttl });
|
|
return result !== null;
|
|
}
|
|
|
|
async setIfNotExists<T = any>(key: string, value: T, ttl?: number): Promise<boolean> {
|
|
if (this.cache.setIfNotExists) {
|
|
return this.cache.setIfNotExists(key, value, ttl);
|
|
}
|
|
// Fallback implementation
|
|
const result = await this.cache.set(key, value, { onlyIfNotExists: true, ttl });
|
|
return result !== null;
|
|
}
|
|
|
|
async replace<T = any>(key: string, value: T, ttl?: number): Promise<T | null> {
|
|
if (this.cache.replace) {
|
|
return this.cache.replace(key, value, ttl);
|
|
}
|
|
// Fallback implementation
|
|
return this.cache.set(key, value, ttl);
|
|
}
|
|
|
|
async updateField<T = any>(key: string, updater: (current: T | null) => T, ttl?: number): Promise<T | null> {
|
|
if (this.cache.updateField) {
|
|
return this.cache.updateField(key, updater, ttl);
|
|
}
|
|
// Fallback implementation
|
|
const current = await this.cache.get<T>(key);
|
|
const updated = updater(current);
|
|
return this.cache.set(key, updated, ttl);
|
|
}
|
|
|
|
/**
|
|
* Get the actual Redis key with prefix
|
|
*/
|
|
getKey(key: string): string {
|
|
return `${this.prefix}:${key}`;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Factory function to create service cache
|
|
*/
|
|
export function createServiceCache(
|
|
serviceName: string,
|
|
redisConfig: RedisConfig,
|
|
options: { global?: boolean; logger?: any } = {}
|
|
): ServiceCache {
|
|
return new ServiceCache(serviceName, redisConfig, options.global, options.logger);
|
|
} |