UNPKG

backend-mcp

Version:

Generador automático de backends con Node.js, Express, Prisma y módulos configurables. Servidor MCP compatible con npx para agentes IA. Soporta PostgreSQL, MySQL, MongoDB y SQLite.

566 lines (435 loc) 14.5 kB
# 📦 Módulo cache **Versión:** 1.0.0 **Categoría:** performance **Descripción:** Sistema de caché distribuido con Redis y estrategias avanzadas ## 📊 Estado del Módulo | Componente | Estado | |------------|--------| | Script de inicialización | ✅ Disponible | | Templates | ❌ Faltante | | Ejemplos | ❌ Faltante | ## 🔗 Dependencias ### Opcionales - `database` - `logging` - `auth` ## 🤖 Triggers para IA Este módulo se activa automáticamente cuando se detectan las siguientes palabras clave: - **user_wants_caching**: true - **needs_performance**: true - **requires_scaling**: true - **has_high_traffic**: true - **undefined**: undefined ## ✨ Características - redis-integration - memory-caching - distributed-caching - cache-invalidation - cache-warming - cache-statistics - cache-compression - cache-serialization - ttl-management - cache-patterns - cache-decorators - cache-interceptors ## 📖 Documentación Completa # Cache Module High-performance caching module for MCP Backend framework with Redis support. ## Features - ✅ Redis integration - ✅ Multiple cache strategies (LRU, TTL, LFU) - ✅ Distributed caching - ✅ Cache invalidation patterns - ✅ Automatic serialization/deserialization - ✅ Cache warming - ✅ Performance metrics - ✅ Cache clustering - ✅ Compression support - ✅ Cache tagging and grouping ## Installation This module is automatically installed when using the MCP Backend Generator. ## Configuration ### Environment Variables **Redis Configuration:** - `REDIS_URL` (required) - Redis connection string - `REDIS_HOST` (optional) - Redis host (default: localhost) - `REDIS_PORT` (optional) - Redis port (default: 6379) - `REDIS_PASSWORD` (optional) - Redis password - `REDIS_DB` (optional) - Redis database number (default: 0) **Cache Configuration:** - `CACHE_TTL` (optional) - Default TTL in seconds (default: 3600) - `CACHE_PREFIX` (optional) - Key prefix (default: 'mcp:') - `CACHE_COMPRESSION` (optional) - Enable compression (default: false) - `CACHE_MAX_MEMORY` (optional) - Max memory usage (default: '100mb') - `CACHE_EVICTION_POLICY` (optional) - Eviction policy (default: 'allkeys-lru') **Cluster Configuration:** - `REDIS_CLUSTER_NODES` (optional) - Cluster nodes (comma-separated) - `REDIS_CLUSTER_PASSWORD` (optional) - Cluster password - `REDIS_SENTINEL_HOSTS` (optional) - Sentinel hosts - `REDIS_SENTINEL_NAME` (optional) - Sentinel master name ### Configuration File ```typescript // src/config/cache.ts export const cacheConfig = { redis: { url: process.env.REDIS_URL, host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), password: process.env.REDIS_PASSWORD, db: parseInt(process.env.REDIS_DB || '0'), retryAttempts: 3, retryDelay: 1000, maxRetriesPerRequest: 3 }, cache: { ttl: parseInt(process.env.CACHE_TTL || '3600'), prefix: process.env.CACHE_PREFIX || 'mcp:', compression: process.env.CACHE_COMPRESSION === 'true', maxMemory: process.env.CACHE_MAX_MEMORY || '100mb', evictionPolicy: process.env.CACHE_EVICTION_POLICY || 'allkeys-lru' }, cluster: { enabled: !!process.env.REDIS_CLUSTER_NODES, nodes: process.env.REDIS_CLUSTER_NODES?.split(',') || [], password: process.env.REDIS_CLUSTER_PASSWORD }, sentinel: { enabled: !!process.env.REDIS_SENTINEL_HOSTS, hosts: process.env.REDIS_SENTINEL_HOSTS?.split(',') || [], name: process.env.REDIS_SENTINEL_NAME || 'mymaster' } }; ``` ## Usage ### Basic Cache Operations ```typescript import { cacheService } from './services/cache'; // Set cache value await cacheService.set('user:123', { name: 'John', email: 'john@example.com' }); // Set with TTL (expires in 1 hour) await cacheService.set('session:abc', sessionData, 3600); // Get cache value const user = await cacheService.get('user:123'); // Get with default value const config = await cacheService.get('config:app', { theme: 'dark' }); // Check if key exists const exists = await cacheService.has('user:123'); // Delete cache key await cacheService.delete('user:123'); // Delete multiple keys await cacheService.deleteMany(['user:123', 'user:456']); // Clear all cache await cacheService.clear(); ``` ### Advanced Cache Operations ```typescript import { cacheService } from './services/cache'; // Increment/Decrement await cacheService.increment('counter:views'); await cacheService.increment('counter:downloads', 5); await cacheService.decrement('counter:stock'); // Hash operations await cacheService.hset('user:123', 'name', 'John Doe'); await cacheService.hset('user:123', { email: 'john@example.com', age: 30 }); const userName = await cacheService.hget('user:123', 'name'); const userHash = await cacheService.hgetall('user:123'); // List operations await cacheService.lpush('notifications:123', notification); const notifications = await cacheService.lrange('notifications:123', 0, 10); // Set operations await cacheService.sadd('tags:post:123', 'javascript', 'nodejs'); const tags = await cacheService.smembers('tags:post:123'); const hasTag = await cacheService.sismember('tags:post:123', 'javascript'); // Sorted set operations await cacheService.zadd('leaderboard', 100, 'user:123'); const topUsers = await cacheService.zrevrange('leaderboard', 0, 9); ``` ### Cache Patterns #### Cache-Aside Pattern ```typescript import { cacheService } from './services/cache'; import { userService } from './services/user'; async function getUser(userId: string) { const cacheKey = `user:${userId}`; // Try to get from cache first let user = await cacheService.get(cacheKey); if (!user) { // Cache miss - get from database user = await userService.findById(userId); if (user) { // Store in cache for future requests await cacheService.set(cacheKey, user, 3600); // 1 hour TTL } } return user; } async function updateUser(userId: string, data: any) { // Update in database const user = await userService.update(userId, data); // Invalidate cache await cacheService.delete(`user:${userId}`); return user; } ``` #### Write-Through Pattern ```typescript async function createUser(userData: any) { // Write to database const user = await userService.create(userData); // Write to cache await cacheService.set(`user:${user.id}`, user, 3600); return user; } ``` #### Write-Behind Pattern ```typescript import { Queue } from 'bull'; const writeQueue = new Queue('cache write-behind'); async function updateUserCache(userId: string, data: any) { // Update cache immediately await cacheService.set(`user:${userId}`, data, 3600); // Queue database update for later await writeQueue.add('update-user', { userId, data }, { delay: 5000, // 5 second delay attempts: 3 }); } ``` ### Cache Tagging and Invalidation ```typescript import { cacheService } from './services/cache'; // Set cache with tags await cacheService.setWithTags( 'product:123', productData, ['products', 'category:electronics'], 3600 ); // Invalidate by tag await cacheService.invalidateTag('products'); await cacheService.invalidateTag('category:electronics'); // Invalidate multiple tags await cacheService.invalidateTags(['products', 'categories']); // Get keys by tag const productKeys = await cacheService.getKeysByTag('products'); ``` ### Cache Warming ```typescript import { cacheService } from './services/cache'; import { productService } from './services/product'; // Warm cache with popular products async function warmProductCache() { console.log('🔥 Warming product cache...'); const popularProducts = await productService.getPopular(100); const promises = popularProducts.map(product => cacheService.set(`product:${product.id}`, product, 7200) // 2 hours ); await Promise.all(promises); console.log(`✅ Warmed cache with ${popularProducts.length} products`); } // Warm cache on application startup export async function warmCache() { await Promise.all([ warmProductCache(), warmCategoryCache(), warmConfigCache() ]); } ``` ### Distributed Locking ```typescript import { cacheService } from './services/cache'; // Acquire distributed lock async function processWithLock(resourceId: string) { const lockKey = `lock:${resourceId}`; const lockValue = `${Date.now()}-${Math.random()}`; const lockTTL = 30; // 30 seconds // Try to acquire lock const acquired = await cacheService.setNX(lockKey, lockValue, lockTTL); if (!acquired) { throw new Error('Resource is locked by another process'); } try { // Process the resource await processResource(resourceId); } finally { // Release lock (only if we still own it) const currentValue = await cacheService.get(lockKey); if (currentValue === lockValue) { await cacheService.delete(lockKey); } } } ``` ### Cache Middleware ```typescript import { Request, Response, NextFunction } from 'express'; import { cacheService } from '../services/cache'; // Cache middleware for Express routes export function cacheMiddleware(ttl: number = 300) { return async (req: Request, res: Response, next: NextFunction) => { const cacheKey = `route:${req.method}:${req.originalUrl}`; try { const cachedResponse = await cacheService.get(cacheKey); if (cachedResponse) { return res.json(cachedResponse); } // Override res.json to cache the response const originalJson = res.json; res.json = function(data: any) { // Cache successful responses if (res.statusCode >= 200 && res.statusCode < 300) { cacheService.set(cacheKey, data, ttl).catch(console.error); } return originalJson.call(this, data); }; next(); } catch (error) { console.error('Cache middleware error:', error); next(); } }; } // Usage in routes app.get('/api/products', cacheMiddleware(600), getProducts); // Cache for 10 minutes ``` ## Performance Monitoring ```typescript // src/utils/cache/metrics.ts import { cacheService } from '../services/cache'; export async function getCacheMetrics() { const info = await cacheService.info(); return { memory: { used: info.used_memory_human, peak: info.used_memory_peak_human, rss: info.used_memory_rss_human }, stats: { hits: info.keyspace_hits, misses: info.keyspace_misses, hitRate: info.keyspace_hits / (info.keyspace_hits + info.keyspace_misses), operations: info.total_commands_processed, connections: info.connected_clients }, keys: { total: info.db0?.keys || 0, expires: info.db0?.expires || 0 }, uptime: info.uptime_in_seconds }; } export async function getCacheHealth() { try { await cacheService.ping(); return { status: 'healthy', timestamp: new Date().toISOString() }; } catch (error) { return { status: 'unhealthy', error: error.message, timestamp: new Date().toISOString() }; } } ``` ## Cache Strategies ### Time-Based Expiration (TTL) ```typescript // Set with TTL await cacheService.set('session:123', sessionData, 1800); // 30 minutes // Set with absolute expiration const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours await cacheService.setWithExpiration('daily:stats', stats, expireAt); ``` ### LRU (Least Recently Used) ```typescript // Configure Redis for LRU eviction // redis.conf: maxmemory-policy allkeys-lru // Access pattern that affects LRU const user = await cacheService.get('user:123'); // Marks as recently used ``` ### Cache Warming Strategies ```typescript // Proactive cache warming setInterval(async () => { await warmPopularContent(); }, 60 * 60 * 1000); // Every hour // Lazy cache warming async function getWithWarmup(key: string, loader: () => Promise<any>) { let value = await cacheService.get(key); if (!value) { value = await loader(); await cacheService.set(key, value, 3600); } return value; } ``` ## Testing ```typescript // tests/cache.test.ts import { cacheService } from '../src/services/cache'; describe('Cache Service', () => { beforeEach(async () => { await cacheService.clear(); }); it('should set and get values', async () => { await cacheService.set('test:key', 'test value'); const value = await cacheService.get('test:key'); expect(value).toBe('test value'); }); it('should handle TTL expiration', async () => { await cacheService.set('test:ttl', 'value', 1); // 1 second TTL let value = await cacheService.get('test:ttl'); expect(value).toBe('value'); // Wait for expiration await new Promise(resolve => setTimeout(resolve, 1100)); value = await cacheService.get('test:ttl'); expect(value).toBeNull(); }); it('should handle cache invalidation', async () => { await cacheService.setWithTags('test:tagged', 'value', ['tag1']); let value = await cacheService.get('test:tagged'); expect(value).toBe('value'); await cacheService.invalidateTag('tag1'); value = await cacheService.get('test:tagged'); expect(value).toBeNull(); }); }); ``` ```bash npm test -- cache ``` ## Dependencies - redis - ioredis - compression (optional) - logging ## Integration - Integrates with all modules requiring caching - Provides session storage for auth module - Caches database query results - Stores temporary data and rate limiting counters ## Error Handling - `CacheConnectionError`: Redis connection failed - `CacheTimeoutError`: Operation timed out - `CacheSerializationError`: Failed to serialize/deserialize data - `CacheLockError`: Failed to acquire distributed lock - `CacheClusterError`: Cluster operation failed ## Best Practices 1. **Key Naming**: Use consistent, hierarchical key naming (e.g., `user:123:profile`) 2. **TTL Management**: Always set appropriate TTL values 3. **Memory Management**: Monitor memory usage and set eviction policies 4. **Error Handling**: Always handle cache failures gracefully 5. **Serialization**: Use efficient serialization for complex objects 6. **Monitoring**: Track cache hit rates and performance metrics 7. **Security**: Don't cache sensitive data without encryption ## License MIT ## 🔗 Enlaces - [Volver al índice de módulos](./README.md) - [Documentación principal](../README.md) - [Código fuente](../../modules/cache/)