@catalyst-team/cache
v0.2.0
Published
Unified caching layer with Redis and in-memory support
Downloads
2,449
Readme
@prediction-router/cache
Three-layer cache package for Prediction Router V2.
Architecture
┌─────────────────────────────────────────────────────────┐
│ Cache Layers │
├─────────────────────────────────────────────────────────┤
│ │
│ L1: Memory (MemoryAdapter) │
│ ├─ In-process Map with TTL │
│ ├─ Fastest (< 1ms) │
│ ├─ Lost on restart │
│ └─ Not shared across instances │
│ │
│ L2: Redis (RedisAdapter) │
│ ├─ Persistent across restarts │
│ ├─ Shared between instances │
│ ├─ Auto-reconnection support │
│ └─ Fast (1-5ms) │
│ │
│ L3: PostgreSQL (handled by application layer) │
│ └─ Long-term storage for historical data │
│ │
└─────────────────────────────────────────────────────────┘Data Flow
Request → L1 (memory) → hit? → return
→ miss ↓
L2 (Redis) → hit? → return + backfill L1
→ miss ↓
Source API → fetch → update L1 + L2 → returnInstallation
pnpm add @prediction-router/cacheUsage
Quick Start
import { createCacheManager } from '@prediction-router/cache';
// Create a cache manager with both L1 and L2
const cache = createCacheManager({
l1: { defaultTTL: 60 }, // 60 seconds
l2: { // Redis config
host: 'localhost',
port: 6379,
defaultTTL: 300, // 5 minutes
},
});
// Basic operations
await cache.set('key', { data: 'value' }, { ttl: 60 });
const value = await cache.get<{ data: string }>('key');
await cache.del('key');
// Cache-aside pattern
const data = await cache.getOrSet('expensive-key', async () => {
return await fetchExpensiveData();
}, { ttl: 300 });Memory-Only Cache
import { MemoryAdapter, CacheManager } from '@prediction-router/cache';
const l1 = new MemoryAdapter({ defaultTTL: 60 });
const cache = new CacheManager({ l1 });
await cache.set('key', 'value');
console.log(await cache.get('key')); // 'value'Redis-Only Cache
import { RedisClient, RedisAdapter, CacheManager } from '@prediction-router/cache';
const client = new RedisClient({
host: process.env.REDIS_HOST || 'localhost',
port: Number(process.env.REDIS_PORT) || 6379,
});
const l2 = new RedisAdapter(client.getClient(), { defaultTTL: 300 });
const cache = new CacheManager({ l2 });
await cache.set('key', { data: 'complex object' }, { ttl: 600 });Advanced Usage
import { CacheManager, MemoryAdapter, RedisAdapter, RedisClient } from '@prediction-router/cache';
// Create adapters
const l1 = new MemoryAdapter({
defaultTTL: 60,
cleanupIntervalMs: 60000, // Clean up expired entries every minute
});
const redisClient = new RedisClient({
host: 'localhost',
port: 6379,
password: process.env.REDIS_PASSWORD,
db: 0,
keyPrefix: 'app:', // All keys will be prefixed with 'app:'
});
const l2 = new RedisAdapter(redisClient.getClient(), {
defaultTTL: 300,
});
// Create manager
const cache = new CacheManager({ l1, l2 });
// Skip L1 for shared data
await cache.set('shared-data', { value: 123 }, {
ttl: 600,
skipL1: true, // Write directly to Redis, skip memory cache
});
// Get cache statistics
const stats = cache.getStats();
console.log(`Hit rate: ${(stats.hitRate * 100).toFixed(2)}%`);
console.log(`L1 hits: ${stats.l1Hits}, L2 hits: ${stats.l2Hits}`);
// Health check
const health = await redisClient.healthCheck();
if (health.healthy) {
console.log(`Redis latency: ${health.latency}ms`);
}API Reference
CacheManager
get<T>(key: string): Promise<T | null>
Get a value from cache (L1 → L2 fallback).
set<T>(key: string, value: T, options?: CacheSetOptions): Promise<void>
Set a value in cache.
Options:
ttl?: number- Time to live in secondsskipL1?: boolean- Skip L1 and write directly to L2
del(key: string): Promise<void>
Delete a value from all cache layers.
exists(key: string): Promise<boolean>
Check if a key exists in any cache layer.
getOrSet<T>(key: string, factory: () => Promise<T>, options?: CacheSetOptions): Promise<T>
Get a value from cache, or fetch and cache it if not found.
clear(): Promise<void>
Clear all cache layers.
getStats(): CacheStats
Get cache statistics (hits, misses, hit rate).
RedisClient
healthCheck(): Promise<{ healthy: boolean; latency?: number; error?: string }>
Perform a health check on Redis connection.
getInfo(): { connected: boolean; status: string; reconnectAttempts: number }
Get connection information.
disconnect(): Promise<void>
Gracefully disconnect from Redis.
Environment Variables
# Redis configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your-password
REDIS_DB=0Testing
# Unit tests (no Redis required)
pnpm test
# Integration tests (requires Redis)
REDIS_HOST=localhost pnpm test:integrationLicense
MIT
