| id |
type |
title |
provider |
status |
integration_type |
created_at |
updated_at |
simco_version |
tags |
| INT-013 |
Integration |
Redis Cache |
Redis |
Planificado |
infrastructure |
2026-01-10 |
2026-01-10 |
4.0.1 |
| redis |
| cache |
| queue |
| bullmq |
| infrastructure |
|
INT-013: Redis Cache
Metadata
| Campo |
Valor |
| Codigo |
INT-013 |
| Proveedor |
Redis |
| Tipo |
Infraestructura |
| Estado |
Planificado |
| Multi-tenant |
Si |
| Epic Relacionada |
MCH-029 |
| Owner |
Backend Team |
1. Descripcion
Redis como servicio de cache y queue para mejorar rendimiento y procesar tareas en background. Utilizado para cache de sesiones, configuracion de tenants, y colas de trabajo con BullMQ.
Casos de uso principales:
- Cache de sesiones JWT
- Cache de configuracion de tenant
- Cache de feature flags
- Queue para emails, webhooks, notificaciones
- Rate limiting counters
- Pub/Sub para eventos real-time
2. Credenciales Requeridas
Variables de Entorno
| Variable |
Descripcion |
Tipo |
Obligatorio |
REDIS_HOST |
Host del servidor Redis |
string |
SI |
REDIS_PORT |
Puerto (default 6379) |
number |
SI |
REDIS_PASSWORD |
Password (si auth habilitado) |
string |
NO |
REDIS_DB |
Database number (0-15) |
number |
NO |
REDIS_TLS |
Usar TLS |
boolean |
NO |
REDIS_URL |
URL completa (alternativa) |
string |
NO |
Ejemplo de .env
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# O usar URL completa
# REDIS_URL=redis://:password@host:6379/0
# Redis Cloud (produccion)
# REDIS_URL=rediss://:password@redis-12345.cloud.redislabs.com:12345
3. Configuracion NestJS
Redis Module
// redis.module.ts
import { Module, Global } from '@nestjs/common';
import { Redis } from 'ioredis';
@Global()
@Module({
providers: [
{
provide: 'REDIS_CLIENT',
useFactory: () => {
return new Redis({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
db: parseInt(process.env.REDIS_DB || '0'),
});
},
},
],
exports: ['REDIS_CLIENT'],
})
export class RedisModule {}
Cache Service
// cache.service.ts
@Injectable()
export class CacheService {
constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) {}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get(key);
return data ? JSON.parse(data) : null;
}
async set(key: string, value: any, ttlSeconds?: number): Promise<void> {
const data = JSON.stringify(value);
if (ttlSeconds) {
await this.redis.setex(key, ttlSeconds, data);
} else {
await this.redis.set(key, data);
}
}
async del(key: string): Promise<void> {
await this.redis.del(key);
}
async invalidatePattern(pattern: string): Promise<void> {
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
}
4. BullMQ Queues
Configuracion
// queue.module.ts
import { BullModule } from '@nestjs/bullmq';
@Module({
imports: [
BullModule.forRoot({
connection: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
},
}),
BullModule.registerQueue(
{ name: 'email' },
{ name: 'webhooks' },
{ name: 'notifications' },
{ name: 'cleanup' },
),
],
})
export class QueueModule {}
Processor
// email.processor.ts
@Processor('email')
export class EmailProcessor {
@Process('send')
async handleSend(job: Job<EmailJobData>) {
const { to, template, variables } = job.data;
await this.emailService.send(to, template, variables);
}
}
Agregar a Queue
// Agregar job
await this.emailQueue.add('send', {
to: 'user@example.com',
template: 'welcome',
variables: { name: 'John' },
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000,
},
});
5. Estructura de Keys
Convenciones de Naming
{prefix}:{scope}:{identifier}:{type}
Ejemplos:
- mch:session:{userId}
- mch:tenant:{tenantId}:config
- mch:tenant:{tenantId}:flags
- mch:rate:{tenantId}:{endpoint}:count
- mch:cache:products:{productId}
Keys por Funcionalidad
| Prefijo |
TTL |
Descripcion |
| mch:session:* |
24h |
Sesiones de usuario |
| mch:tenant:*:config |
1h |
Config de tenant |
| mch:tenant:*:flags |
5m |
Feature flags |
| mch:rate:* |
1m |
Rate limiting |
| mch:cache:* |
15m |
Cache general |
6. Rate Limiting
Implementacion Token Bucket
@Injectable()
export class RateLimitService {
async isAllowed(
tenantId: string,
endpoint: string,
limit: number,
windowSeconds: number,
): Promise<{ allowed: boolean; remaining: number; resetAt: Date }> {
const key = `mch:rate:${tenantId}:${endpoint}`;
const now = Date.now();
const windowStart = now - (windowSeconds * 1000);
// Remover entradas viejas
await this.redis.zremrangebyscore(key, 0, windowStart);
// Contar requests en ventana
const count = await this.redis.zcard(key);
if (count >= limit) {
const oldestEntry = await this.redis.zrange(key, 0, 0, 'WITHSCORES');
const resetAt = new Date(parseInt(oldestEntry[1]) + (windowSeconds * 1000));
return { allowed: false, remaining: 0, resetAt };
}
// Agregar request actual
await this.redis.zadd(key, now, `${now}`);
await this.redis.expire(key, windowSeconds);
return {
allowed: true,
remaining: limit - count - 1,
resetAt: new Date(now + (windowSeconds * 1000)),
};
}
}
7. Multi-tenant
Aislamiento por Prefijo
class TenantCacheService {
private getKey(tenantId: string, key: string): string {
return `mch:tenant:${tenantId}:${key}`;
}
async getTenantConfig(tenantId: string): Promise<TenantConfig> {
const key = this.getKey(tenantId, 'config');
const cached = await this.cache.get<TenantConfig>(key);
if (cached) return cached;
const config = await this.db.getTenantConfig(tenantId);
await this.cache.set(key, config, 3600); // 1 hora
return config;
}
async invalidateTenantCache(tenantId: string): Promise<void> {
await this.cache.invalidatePattern(`mch:tenant:${tenantId}:*`);
}
}
8. Health Check
// redis.health.ts
@Injectable()
export class RedisHealthIndicator extends HealthIndicator {
constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) {
super();
}
async isHealthy(key: string): Promise<HealthIndicatorResult> {
try {
await this.redis.ping();
return this.getStatus(key, true);
} catch (error) {
return this.getStatus(key, false, { error: error.message });
}
}
}
Endpoint
@Get('health')
@HealthCheck()
async check() {
return this.health.check([
() => this.redis.isHealthy('redis'),
]);
}
9. Monitoreo
Metricas
| Metrica |
Descripcion |
Alerta |
| redis_connected |
Conexion activa |
false |
| redis_memory_bytes |
Memoria usada |
> 80% max |
| redis_keys_total |
Total de keys |
- |
| redis_cache_hits |
Cache hits |
- |
| redis_cache_misses |
Cache misses |
ratio < 80% |
| redis_queue_length |
Jobs en queue |
> 1000 |
Comandos de Monitoreo
# Info general
redis-cli INFO
# Memory
redis-cli INFO memory
# Keys por patron
redis-cli KEYS "mch:tenant:*" | wc -l
# Queue length
redis-cli LLEN bull:email:wait
10. Testing
Docker Compose
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
Mock para Tests
// test/mocks/redis.mock.ts
export const mockRedis = {
get: jest.fn(),
set: jest.fn(),
setex: jest.fn(),
del: jest.fn(),
keys: jest.fn().mockResolvedValue([]),
ping: jest.fn().mockResolvedValue('PONG'),
};
11. Referencias
Ultima actualizacion: 2026-01-10
Autor: Backend Team