Ir al contenido
RT
Volver

Cloudflare Queues: responde rápido, procesa después

Una de las reglas más útiles en backend:

Si el usuario no necesita el resultado inmediato, no bloquees la request esperando ese trabajo.

Ejemplos típicos:

Cloudflare Queues te ayuda a separar dos caminos:

experiencia del usuario  ->  request rápida
procesamiento interno    ->  mensajes en cola

Si vienes de Workers, la idea es: el Worker sigue siendo la entrada; la Queue se lleva el trabajo que no debe vivir dentro del fetch.

Términos (déjalo claro, no lo inventes)

TérminoSignificado
Queuecola de mensajes
Messageunidad de trabajo
Producercódigo que manda mensajes
Consumercódigo que lee y procesa
Retryreintento controlado
DLQDead Letter Queue — cola para mensajes que agotaron retries

Flujo base:

request -> producer -> Queue -> consumer

Modelo mental: checkout

Cuando alguien compra, tu sistema puede necesitar:

  1. confirmar el request
  2. responder al usuario
  3. mandar recibo
  4. actualizar CRM
  5. registrar analytics
  6. avisar a soporte

No todo eso tiene que ocurrir antes de responder.

Puedes devolver:

{ "accepted": true }

y dejar jobs en una Queue para después.

Producer y consumer en Workers

Producer desde fetch():

type ReceiptJob = {
  orderId: string;
  email: string;
};

export interface Env {
  RECEIPT_QUEUE: Queue<ReceiptJob>;
}

export default {
  async fetch(request, env): Promise<Response> {
    const job = await request.json<ReceiptJob>();

    await env.RECEIPT_QUEUE.send({
      orderId: job.orderId,
      email: job.email,
    });

    return Response.json({ accepted: true }, { status: 202 });
  },
} satisfies ExportedHandler<Env>;

Consumer:

export default {
  async queue(batch, env, ctx): Promise<void> {
    for (const message of batch.messages) {
      try {
        await sendReceipt(message.body);
        message.ack();
      } catch (error) {
        console.error("receipt_failed", {
          orderId: message.body.orderId,
          error,
        });
        message.retry({ delaySeconds: 30 });
      }
    }
  },
} satisfies ExportedHandler<Env, ReceiptJob>;

Concepto:

fetch produce
queue consume

RECEIPT_QUEUE existe como binding. El tipo del message es parte del diseño, no un detalle cosmético.

Por qué a menudo 202 Accepted

202 comunica:

Recibí la solicitud y la acepté para procesamiento; el trabajo completo puede seguir pendiente.

No siempre hace falta. Para flujos async suele ser más honesto que fingir un 200 OK cuando el recibo, el CRM y el webhook aún no corrieron.

Retries: los errores no desaparecen

Una Queue no elimina fallos. Los saca del camino principal y te da un mecanismo para reintentar.

Los sistemas externos fallan:

Si todo eso vive dentro de la request, el usuario paga el costo. Con una Queue, puedes responder rápido y procesar con más control.

DLQ: no es basurero, es evidencia

DLQ = Dead Letter Queue: mensajes que llegaron al límite de retries.

No la pienses como “donde cae lo malo”.

Piensa:

bandeja de investigación para mensajes que no pudieron procesarse.

Una DLQ te ayuda a responder:

Sin DLQ, muchos fallos async quedan invisibles o se pierden.

Idempotencia: asume re-entrega

En sistemas async debes asumir que un mensaje puede procesarse más de una vez.

Idempotente:

procesar el mismo mensaje dos veces no debe duplicar un efecto peligroso.

Malo:

send receipt

Mejor:

send receipt for orderId if receipt_sent = false

O:

add CRM event with idempotency key = jobId

No basta con “usar Queue”. Hay que diseñar el consumer.

Cuándo usaría Queues

Buenas señales:

Patrones comunes:

checkout -> Queue -> receipt email
upload   -> Queue -> image resize
webhook  -> Queue -> normalization
form     -> Queue -> CRM sync
analytics-> Queue -> batch write

Cuándo no metería todo en una Queue

Puede no ser el patrón correcto si:

ProblemaMejor herramienta
Multi-step durableWorkflows
Estado por entidadDurable Objects
Request simpleun Worker basta

Checklist de producción

  1. Define el tipo del message.
  2. Incluye jobId, orderId u otra key estable.
  3. Decide qué errores se reintentan.
  4. Decide qué errores van a DLQ.
  5. Haz el consumer idempotente.
  6. Loguea con contexto.
  7. Mide backlog, retries y fallos.
  8. Documenta quién produce y quién consume.

Una Queue sin observabilidad es una caja negra. Con logs y DLQ se vuelve herramienta operativa.

La frase para recordar

Worker fetch = producer
Queue = buffer de mensajes
queue handler = consumer
retry = reintento controlado
DLQ = investigación (y posible reproceso)

Responde rápido. Procesa después. Observa siempre.


Serie Cloudflare para builders: Workers · Bindings · Durable Objects · Queues

Docs: Queues · How Queues works · Getting started · Batching, retries, delays · Dead Letter Queues


Share this post on:

Siguiente
Cómo arranco proyectos indie: Better-T-Stack y lo que elijo de adentro