aws.crafter.run

Comunicación

Cola punto a punto

También llamado Point-to-point channel / competing consumers

Desacoplar a quien pide el trabajo de quien lo hace con una única cola, de forma que el pico se absorba, el trabajo se reparta y añadir capacidad sea añadir consumidores.

Servicios
DirectoVerificado el

El problema

Tu API recibe una petición que dispara un trabajo lento: generar un informe, redimensionar un vídeo, llamar a un ERP que tarda ocho segundos.

Hacerlo en la misma invocación tiene tres problemas simultáneos:

Llamar directamente a otra Lambda no arregla nada: sigue sin haber sitio donde el trabajo espere.

La solución

Una cola entre quien pide y quien hace.

productorSQSuna sola colaLa misma función, en paraleloprocesar #1mensaje Aprocesar #2mensaje Bprocesar #3mensaje CCada mensaje lo recibe uno solo · para escalar se añaden consumidores, no colas
Una cola reparte; no duplica. Tres consumidores sobre la misma cola son tres obreros turnándose, no tres suscriptores. Si lo que querías era que los tres vieran todos los mensajes, esto no es una cola: es un fan-out.

El productor deposita el mensaje y responde 202 en milisegundos. Uno o varios consumidores lo recogen al ritmo que puedan. Y aquí está la propiedad que define el patrón y que se confunde constantemente:

Una cola reparte; no duplica. Cada mensaje lo recibe un solo consumidor.

Tres funciones consumiendo la misma cola no son tres suscriptores: son tres obreros turnándose. Por eso el patrón también se llama competing consumers, y por eso escalar es simplemente añadir consumidores, no colas.

Lo que ganas:

Cuándo usarlo

Cuándo NO usarlo

Cómo implementarlo

  1. Crea la DLQ antes que la cola y dale más retención que a la de origen.
  2. maxReceiveCount entre 3 y 5.
  3. Visibility timeout cómodamente por encima del timeout del consumidor —no ajustado— para que un proceso lento no genere duplicados.
  4. Long polling a 20 segundos: menos respuestas vacías, menos latencia y menos factura.
  5. reportBatchItemFailures activado, siempre.
  6. Consumidor idempotente: la entrega es al menos una vez.
  7. Concurrencia reservada en el consumidor si detrás hay algo que no escala.
  8. Alarmas sobre la profundidad de la cola, la edad del mensaje más antiguo y la DLQ.

El código

// ── Productor: deposita y responde. No espera a nadie. ─────────────
export const pedirInforme = async (evento: APIGatewayProxyEventV2) => {
  const id = crypto.randomUUID();

  await sqs.send(new SendMessageCommand({
    QueueUrl: process.env.COLA,
    MessageBody: JSON.stringify({ id, ...JSON.parse(evento.body!) }),
  }));

  // 202: aceptado, todavia no hecho. El cliente consulta luego por id.
  return { statusCode: 202, body: JSON.stringify({ id }) };
};
// ── Consumidor: uno de varios, compitiendo por los mensajes ────────
export const generarInforme = async (e: SQSEvent): Promise<SQSBatchResponse> => {
  const fallidos: { itemIdentifier: string }[] = [];

  for (const r of e.Records) {
    const trabajo = JSON.parse(r.body);
    try {
      // Al menos una vez: este mensaje puede llegar dos veces.
      if (await yaGenerado(trabajo.id)) continue;
      await generar(trabajo);
    } catch {
      // Solo este vuelve a la cola; los otros nueve del lote no.
      fallidos.push({ itemIdentifier: r.messageId });
    }
  }

  return { batchItemFailures: fallidos };
};
// ── Infraestructura ────────────────────────────────────────────────
const dlq = new sqs.Queue(this, 'InformesDlq', {
  retentionPeriod: cdk.Duration.days(14), // mas que la de origen
});

const cola = new sqs.Queue(this, 'Informes', {
  retentionPeriod: cdk.Duration.days(4),
  visibilityTimeout: cdk.Duration.minutes(6), // holgado sobre el timeout
  deadLetterQueue: { queue: dlq, maxReceiveCount: 3 },
});

consumidor.addEventSource(new SqsEventSource(cola, {
  batchSize: 10,
  reportBatchItemFailures: true,
}));

Te va a morder

Coste

Concepto Precio aproximado
Peticiones de SQS estándar ~0,40 USD / millón
Primer millón al mes gratis
Almacenar mensajes sin coste
Invocaciones del consumidor las normales de Lambda

La cola es de las piezas más baratas de AWS: no cobra por existir ni por guardar, solo por peticiones. Dos cosas mueven la factura más que el precio unitario:

Los lotes. Diez mensajes en una llamada son una petición, no diez. Si controlas el productor, batear es la optimización más rentable del patrón.

El sondeo vacío. Una cola ociosa conectada a Lambda genera ReceiveMessage las veinticuatro horas. Long polling a 20 segundos reduce ese goteo a la vigésima parte de lo que costaría sondeando cada segundo.

Fuentes

Garantías de entrega, desorden y comportamiento de las colas estándar: Amazon SQS standard queues. Visibility timeout, retención, retraso máximo, tamaño de lote y long polling: Amazon SQS message quotas. Mensajes en vuelo y comportamiento con short polling: Amazon SQS standard queue quotas. Regla de facturación por porciones de 64 KB: SQS pricing.

Patrones relacionados

Un enlace sin la relación nombrada es un "ver también". Aquí cada uno dice qué relación tiene y por qué.