- 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>
225 lines
6.0 KiB
Markdown
225 lines
6.0 KiB
Markdown
# 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:
|
|
|
|
1. **Job Queue**
|
|
- Cada video subido crea un job en la cola
|
|
- Workers procesan jobs en background
|
|
- Jobs tienen estados: PENDING, PROCESSING, DONE, FAILED
|
|
|
|
2. **Notificaciones**
|
|
- Push notification cuando el resultado esta listo
|
|
- Polling opcional para clientes sin push
|
|
|
|
3. **Reintentos**
|
|
- Jobs fallidos se reintentan automaticamente
|
|
- Backoff exponencial para APIs externas
|
|
- Max 3 reintentos por job
|
|
|
|
4. **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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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](https://docs.bullmq.io/)
|
|
- [MII-005](../01-epicas/MII-005-procesamiento-ia.md) - Procesamiento IA
|
|
- [INT-006](../02-integraciones/INT-006-ia-provider.md) - Integracion IA
|
|
|
|
---
|
|
|
|
**Ultima Actualizacion:** 2026-01-10
|