Express.js
Background jobs y colas
Explica cuándo sacar trabajo del ciclo request-response y cómo coordinar colas, retries, idempotencia, estados, workers y observabilidad.
- Última actualización
- Actualizada
- Nivel
- Aplicación
Express.js
Explica cuándo sacar trabajo del ciclo request-response y cómo coordinar colas, retries, idempotencia, estados, workers y observabilidad.
Responder HTTP y ejecutar trabajo durable son ciclos distintos:
request
↓
validar y persistir intención
↓
encolar job
↓
responder 202/201
worker
↓
reservar job
↓
ejecutar
↓
complete, retry o dead-letterEmails, reportes, thumbnails, sincronizaciones y webhooks salientes pueden tardar o depender de servicios inestables. Ejecutarlos dentro del handler aumenta latencia y pierde trabajo si el proceso cae después de responder.
Una cola debe conservar mensajes fuera de la memoria de una instancia. Opciones incluyen Redis/BullMQ, RabbitMQ, SQS, Kafka o tablas PostgreSQL según requisitos.
La elección depende de entrega, orden, throughput, retención y operación; no solo de popularidad.
El caso de uso crea una intención:
await jobs.add('send-order-confirmation', {
orderId: order.id,
tenantId,
}, {
jobId: `order-confirmation:${order.id}`,
attempts: 5,
backoff: { type: 'exponential', delay: 1_000 },
});Un job ID estable ayuda a deduplicar, pero debes conocer la semántica real de la librería.
worker.process(async (job) => {
const input = JobSchema.parse(job.data);
await sendConfirmation.execute(input);
});Los datos de la cola también requieren validación: pueden ser antiguos, corruptos o pertenecer a otra versión.
Muchos sistemas pueden ejecutar un job más de una vez: el worker termina el efecto pero cae antes del acknowledgement.
Por eso el handler del job debe ser idempotente:
El problema clásico:
COMMIT order
↓ proceso cae
publish job nunca ocurreOutbox inserta el cambio y el evento en la misma transacción:
INSERT INTO orders ...;
INSERT INTO outbox_events (id, type, payload) ...;
COMMIT;Un publisher posterior entrega el evento y marca progreso.
Clasifica errores:
Aplica backoff y jitter. Respeta Retry-After cuando corresponda.
Después del máximo de intentos, conserva el job y contexto seguro para revisión. Una dead-letter queue sin alertas es solo almacenamiento de fallos olvidados.
Si el orden importa por entidad, una cola global no lo garantiza automáticamente. Usa partition key, secuencia o verificación de versión.
Evita exigir orden global si solo necesitas orden por orderId o tenant.
Configura concurrency según recursos downstream. Cien workers de email pueden superar límites del proveedor o saturar el pool de DB.
Mide tiempo de cola, ejecución y capacidad de dependencias.
Para trabajos largos:
No mantengas una transacción abierta durante todo el job.
Tareas recurrentes necesitan evitar ejecuciones duplicadas entre instancias. Usa scheduler coordinado, lock distribuido o servicio de plataforma.
Generar reporte de ventas:
report_request./reports/:id.Si falla, el recurso muestra failed y un código público.
La edad del más antiguo suele ser más útil que solo cantidad.
El worker debe dejar de reservar nuevos jobs y terminar o liberar los activos. Un kill abrupto puede producir reejecución; el job debe tolerarla.
void sendEmail() después de responder.WebSockets, SSE y tiempo real compara conexiones persistentes y entrega de eventos al cliente.