- Backend NestJS con módulos de autenticación, inventario, créditos - Frontend React con dashboard y componentes UI - Base de datos PostgreSQL con migraciones - Tests E2E configurados - Configuración de Docker y deployment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.0 KiB
6.0 KiB
ADR-0002: Procesamiento Asincrono
id: ADR-0002 type: ADR status: Aceptado created_date: 2026-01-10 updated_date: 2026-01-10 decision_date: 2026-01-10 deciders: ["Tech Lead"]
Estado
Aceptado
Contexto
El procesamiento de video con IA para deteccion de productos:
- Toma entre 30 segundos y 3 minutos
- Depende de APIs externas (OpenAI, Claude)
- Puede fallar por timeout o errores de red
- Necesita reintentos y manejo de errores
Procesar sincronamente en la request HTTP:
- Causaria timeouts
- Bloquearia recursos del servidor
- Proporcionaria mala experiencia de usuario
Decision
Implementar procesamiento asincrono con Bull/Redis para:
-
Job Queue
- Cada video subido crea un job en la cola
- Workers procesan jobs en background
- Jobs tienen estados: PENDING, PROCESSING, DONE, FAILED
-
Notificaciones
- Push notification cuando el resultado esta listo
- Polling opcional para clientes sin push
-
Reintentos
- Jobs fallidos se reintentan automaticamente
- Backoff exponencial para APIs externas
- Max 3 reintentos por job
-
Monitoreo
- Dashboard de Bull para ver estado de jobs
- Alertas por tasa de error alta
- Metricas de tiempo de procesamiento
Opciones Consideradas
Opcion A: Procesamiento Sincrono
| Aspecto | Valor |
|---|---|
| Complejidad | Baja |
| UX | Mala (esperar 1-3 min) |
| Escalabilidad | Baja |
| Manejo errores | Dificil |
Descartada: Inaceptable para UX y escalabilidad.
Opcion B: AWS SQS + Lambda
| Aspecto | Valor |
|---|---|
| Complejidad | Alta |
| Escalabilidad | Muy alta |
| Costo | Variable |
| Vendor lock-in | Alto |
Descartada: Sobre-ingenieria para MVP, lock-in.
Opcion C: Bull + Redis (Seleccionada)
| Aspecto | Valor |
|---|---|
| Complejidad | Media |
| Escalabilidad | Alta |
| Costo | Bajo (Redis ya existe) |
| Control | Total |
Seleccionada: Balance ideal para MVP con path de escalamiento.
Consecuencias
Positivas
- UX fluida: Usuario no espera, recibe notificacion
- Resiliente: Reintentos automaticos
- Escalable: Agregar workers segun demanda
- Observable: Dashboard, metricas, logs
- Flexible: Prioridades, delays, dependencias
Negativas
- Complejidad: Otro componente (Redis)
- Eventual consistency: Resultado no inmediato
- Debugging: Mas dificil seguir el flujo
- Infraestructura: Redis debe ser HA en produccion
Arquitectura
┌─────────────────────────────────────────────────────────────────┐
│ PROCESAMIENTO ASINCRONO │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ API │───▶│ Queue │───▶│ Worker │───▶│ Notifier │ │
│ │ (NestJS) │ │ (Bull) │ │ (Bull) │ │ (FCM) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ Crear job Redis Procesar IA Push notif │
│ status=PENDING almacena actualizar DB al usuario │
│ jobs status=DONE │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementacion
Definicion del Job
// jobs/inventory-processing.processor.ts
@Processor('inventory')
export class InventoryProcessor {
@Process('process-video')
async processVideo(job: Job<ProcessVideoData>) {
const { sessionId, videoId } = job.data;
try {
// 1. Actualizar status
await this.updateStatus(sessionId, 'PROCESSING');
// 2. Descargar video de S3
const video = await this.s3Service.download(videoId);
// 3. Extraer frames
const frames = await this.extractFrames(video);
// 4. Procesar con IA
const detections = await this.iaService.detectProducts(frames);
// 5. Consolidar resultados
const items = await this.consolidate(detections);
// 6. Guardar resultados
await this.saveResults(sessionId, items);
// 7. Calcular COGS
await this.calculateCOGS(sessionId, frames.length);
// 8. Actualizar status
await this.updateStatus(sessionId, 'DONE');
// 9. Notificar usuario
await this.notify(sessionId);
} catch (error) {
await this.updateStatus(sessionId, 'FAILED');
throw error; // Bull reintentara
}
}
}
Configuracion de Cola
// queue.module.ts
BullModule.registerQueue({
name: 'inventory',
defaultJobOptions: {
attempts: 3,
backoff: {
type: 'exponential',
delay: 5000, // 5s, 10s, 20s
},
removeOnComplete: 100,
removeOnFail: 50,
},
});
Metricas
| Metrica | Objetivo |
|---|---|
| Tiempo promedio | < 2 min |
| Tasa de exito | > 95% |
| Jobs en cola | < 100 |
| Latencia cola | < 30s |
Referencias
- Bull Documentation
- MII-005 - Procesamiento IA
- INT-006 - Integracion IA
Ultima Actualizacion: 2026-01-10