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
Markdown
**Versión:** 1.0.0
**Categoría:** performance
**Descripción:** Sistema de caché distribuido con Redis y estrategias avanzadas
| Componente | Estado |
|------------|--------|
| Script de inicialización | ✅ Disponible |
| Templates | ❌ Faltante |
| Ejemplos | ❌ Faltante |
- `database`
- `logging`
- `auth`
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
- redis-integration
- memory-caching
- distributed-caching
- cache-invalidation
- cache-warming
- cache-statistics
- cache-compression
- cache-serialization
- ttl-management
- cache-patterns
- cache-decorators
- cache-interceptors
High-performance caching module for MCP Backend framework with Redis support.
- ✅ 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'
}
};
```
```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();
```
```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);
```
```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;
}
```
```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;
}
```
```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
});
}
```
```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');
```
```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()
]);
}
```
```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);
}
}
}
```
```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
```
```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()
};
}
}
```
```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);
```
```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
```
```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;
}
```
```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
```
- redis
- ioredis
- compression (optional)
- logging
- Integrates with all modules requiring caching
- Provides session storage for auth module
- Caches database query results
- Stores temporary data and rate limiting counters
- `CacheConnectionError`: Redis connection failed
- `CacheTimeoutError`: Operation timed out
- `CacheSerializationError`: Failed to serialize/deserialize data
- `CacheLockError`: Failed to acquire distributed lock
- `CacheClusterError`: Cluster operation failed
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/)