Monitorización18 de octubre de 2026 11 min de lectura

Cómo monitorizar un pipeline SQS → Lambda → DynamoDB sin escribir código

Una cola, una función y una tabla pueden fallar de cinco maneras distintas, y alguna no deja ni rastro en las métricas. Te contamos qué señal delata cada fallo, cómo vigilar los tres recursos con AAA Sentinel sin tocar tu código y cómo un flujo de prueba con traceId te dice en qué paso exacto se rompe la cadena.

Este artículo también está disponible en:enfrpt

Un pedido entra en una cola SQS, una Lambda lo procesa y el resultado se guarda en DynamoDB. Es uno de los patrones más comunes en AWS, y también uno de los que fallan en silencio: la cola acepta mensajes aunque nadie los lea, la función reintenta hasta que algo sale bien y la tabla no se queja de lo que nunca le llegó. En este artículo repasamos cómo falla esta cadena, qué señal delata cada fallo y cómo vigilarla con AAA Sentinel paso a paso, con nombres de ejemplo que puedes sustituir por los tuyos.

El pipeline del ejemplo

Usamos una tienda ficticia con este pipeline en la cuenta 123456789012, región eu-west-1:

Pipeline de ejemplo

Cola de entrada
orders-queue, con redrive a la DLQ tras 5 recepciones
Cola de mensajes fallidos (DLQ)
orders-dlq
Función
process-order, conectada a la cola con un event source mapping
Tabla
orders, con clave de partición orderId (texto)

Cómo falla un pipeline así

Casi todos los incidentes de esta cadena caen en uno de estos cinco casos:

  • Los mensajes se acumulan: la función está parada, el event source mapping está desactivado o la concurrencia reservada se quedó en cero. La cola sigue aceptando pedidos y nadie los procesa.
  • Mensajes envenenados: un mensaje con un formato que la función no entiende falla en cada intento. Tras la visibilidad agotada vuelve a la cola, y al llegar al máximo de recepciones de la redrive policy acaba en la DLQ. Si la función procesa lotes sin informar de los fallos por mensaje (ReportBatchItemFailures), un solo mensaje malo hace reintentar el lote entero.
  • Errores y limitaciones de la función: excepciones, tiempos agotados o throttling por falta de concurrencia. Un pico de errores suele verse; un 3 % constante, no.
  • Reintentos que esconden el fallo: el mensaje falla dos veces y a la tercera sale bien. No llega a la DLQ y la tasa de errores no llama la atención, pero el pedido se guarda minutos tarde.
  • Ítems que nunca se escriben: la función termina bien, pero no escribe. Una variable de entorno que apunta a otra tabla, una escritura condicional que se descarta, una excepción capturada que solo deja una línea en el log. Ninguna métrica se mueve.

Qué señal delata cada fallo

CloudWatch ya publica casi todo lo necesario. Esta es la señal que mejor detecta cada caso:

Fallo → señal

Mensajes acumulados
SQS ApproximateAgeOfOldestMessage de orders-queue. Mejor que contar mensajes: el número sube con la carga, la antigüedad sube cuando nadie consume
Mensajes envenenados
ApproximateNumberOfMessagesVisible de orders-dlq mayor que cero. Un mensaje ahí ya agotó todos sus reintentos
Errores de la función
Lambda Errors frente a Invocations de process-order, como porcentaje
Limitaciones de la función
Lambda Throttles de process-order
Función lenta
Lambda Duration p95 cerca del timeout configurado: cuando lo alcance, cada intento será un error
Tabla saturada
DynamoDB ReadThrottleEvents y WriteThrottleEvents de orders
Ítems que no se escriben y reintentos que retrasan
Ninguna métrica: solo se ve mandando una prueba y comprobando que llega al final

Las métricas dicen que cada pieza está encendida, no que la cadena funcione. Una cola vacía, una función sin errores y una tabla sin throttling son compatibles con cero pedidos guardados.

Paso 1: vigilar la cola, la DLQ, la función y la tabla

Esta parte no necesita código. En Monitorización → Nuevo → Monitor elige el tipo Recurso cloud, la cuenta ya conectada, la región y el recurso de la lista (o pega su ARN). Si la cuenta todavía no concede el acceso de lectura de Monitorización, el formulario lo dice y ofrece «Conceder el permiso». AAA Sentinel lee las métricas con ese rol de solo lectura, sin agentes. Con tres monitores cubres los cuatro recursos:

  • orders-queue (Cola SQS): se da por caída si hay algún mensaje en su cola de mensajes fallidos o si el mensaje más antiguo lleva más de la antigüedad máxima sin consumirse (60 minutos si lo dejas vacío). La DLQ no necesita monitor propio: el monitor de la cola la encuentra en su redrive policy y lee cuántos mensajes tiene directamente de SQS, sin esperar a CloudWatch.
  • process-order (Función Lambda): se da por caída si falla o se limita el 10 % o más de sus ejecuciones (con al menos 10), o si fallan todas habiendo entre 3 y 9. El porcentaje se ajusta en «Porcentaje de fallos (%)».
  • orders (Tabla DynamoDB): se da por caída si rechaza por capacidad más de 10 peticiones en el periodo, sumando lecturas y escrituras. Se ajusta en «Rechazos por capacidad tolerados»; en una tabla bajo demanda con poco tráfico puedes bajarlo a 0.

El monitor de la función mira errores, limitaciones y, en orígenes de tipo stream, la antigüedad del iterador; no mira la duración. Si la p95 de Duration te preocupa por sí sola, una alarma de CloudWatch es la herramienta. Para lo que nota el cliente, el tiempo de punta a punta, está el flujo del paso siguiente.

Paso 2: qué hace un flujo

Un flujo comprueba la cadena entera con una transacción de prueba. Cada 15 minutos, como mínimo, AAA Sentinel manda un mensaje a orders-queue con un traceId propio (32 caracteres hexadecimales) y comprueba que llega a cada paso dentro de su plazo. El mensaje lleva el traceId en el atributo de mensaje x-aaa-trace y en el cuerpo como aaaTraceId, junto a un traceparent W3C; si la cola es FIFO, la prueba va en su propio grupo de mensajes. Los pasos posibles son una línea en CloudWatch Logs, un ítem de DynamoDB, un objeto de S3 o un callback que tu código hace a una URL nuestra, hasta cinco. Para este pipeline usamos dos: la línea en el log de process-order y el ítem en orders.

Paso 3: preparar la función (aquí sí hay unas líneas)

Para ser honestos con el título: vigilar los recursos no requiere código, pero el flujo necesita que la función reconozca la prueba. La prueba recorre tu cadena real, así que tu código tiene que tratarla sin efectos reales (sin cobros, correos ni pedidos) y dejar dos huellas que podamos encontrar:

Node.js con @aaa-sentinel/healthjavascript
// npm install @aaa-sentinel/health
import { propagateTrace } from '@aaa-sentinel/health'
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'

const db = DynamoDBDocumentClient.from(new DynamoDBClient({}))

export const handler = async (event) => {
  for (const record of event.Records) {
    const trace = propagateTrace(record)
    if (trace) {
      trace.log('order received')
      // Prueba de AAA Sentinel: sin cobros ni correos, y con el traceId en la clave para que el paso de DynamoDB la encuentre.
      await db.send(new PutCommand({
        TableName: process.env.ORDERS_TABLE,
        Item: {
          orderId: `aaa-test-${trace.traceId}`,
          ...trace.fields,
          // Atributo TTL de la tabla: DynamoDB borra solo los ítems de prueba.
          expiresAt: Math.floor(Date.now() / 1000) + 86400,
        },
      }))
      continue
    }
    // Aquí sigue tu lógica real con el pedido.
  }
}
Python, sin dependenciaspython
import json, os, time
import boto3

table = boto3.resource("dynamodb").Table(os.environ["ORDERS_TABLE"])

def handler(event, context):
    for record in event["Records"]:
        attribute = record.get("messageAttributes", {}).get("x-aaa-trace", {})
        trace_id = attribute.get("stringValue")
        if trace_id:
            print(json.dumps({"msg": "order received", "traceId": trace_id}))
            # Prueba de AAA Sentinel: sin efectos reales, y con el traceId en la clave del ítem.
            table.put_item(Item={"orderId": f"aaa-test-{trace_id}", "aaaTraceId": trace_id, "expiresAt": int(time.time()) + 86400})
            continue
        # Aquí sigue tu lógica real con el pedido.
  • Una línea en su log con el traceId. Si la función ya escribe en el log el evento o el cuerpo del mensaje, el traceId aparece ahí y no hay que tocar nada para este paso.
  • Un ítem cuya clave primaria contenga el traceId. El paso de DynamoDB no busca por un atributo: hace un GetItem con lectura consistente sobre la clave que tú defines, con {traceId} sustituido por el de cada prueba, y solo pide los atributos de la clave, no el contenido del ítem. Por eso la clave tiene que ser de texto e incluir el traceId (aquí, orderId = aaa-test-{traceId}); guardarlo solo en un atributo no vale, y tampoco se busca en índices secundarios.

propagateTrace lee el traceId de cualquier evento (SQS, SNS, EventBridge, DynamoDB Streams, HTTP) y devuelve las cabeceras, los atributos de mensaje y los campos para pasarlo al siguiente paso; trace.log escribe una línea JSON que termina con el traceId. Activa el TTL de la tabla sobre expiresAt para que los ítems de prueba no se acumulen.

Paso 4: crear el flujo en el asistente

En Monitorización → Nuevo → Flujo, el asistente tiene tres pasos:

Flujo de ejemplo · valores

Qué inicia el flujo
Cola SQS · arn:aws:sqs:eu-west-1:123456789012:orders-queue
Contenido de la prueba
{"type": "order", "test": true}
Paso 1 · Línea en los logs
arn:aws:logs:eu-west-1:123456789012:log-group:/aws/lambda/process-order:* · texto {traceId} · plazo 60 s
Paso 2 · Ítem de DynamoDB
Tabla orders · orderId = aaa-test-{traceId} · plazo 120 s
Cada cuánto se prueba
Cada 15 minutos
  • Dónde empieza la cadena: eliges la cuenta, la región y en «Qué inicia el flujo» la opción Cola SQS, pegas el ARN de orders-queue y pulsas «Descubrir la cadena». Solo se lee la configuración; todavía no se lanza ninguna prueba. AAA Sentinel sigue el event source mapping hasta process-order y propone un paso de logs por cada función, con el texto {traceId} y un plazo de 60 segundos.
  • Qué se comprueba: revisas el paso de logs y añades el último en «Añadir el último paso» → Ítem de DynamoDB. La lista de tablas pone primero las que aparecen en la configuración de la función («aparece en tu función»): si ORDERS_TABLE apunta a orders, la verás arriba. En «Clave del ítem» escribes orderId y aaa-test-{traceId}. En «Contenido de la prueba» puedes añadir lo que tu código necesite para no descartar el mensaje.
  • Nombre y alertas: el nombre, la severidad y cada cuánto se prueba, con un mínimo de 15 minutos, porque cada prueba escribe en tu cadena y hace unas veinte lecturas en tu cuenta.

Cada plazo se cuenta desde el disparo, no desde el paso anterior, va de 10 a 900 segundos y no puede ser menor que el del paso anterior. Con 60 y 120 segundos, el pedido tiene dos minutos para estar en la tabla.

Paso 5: el rol de flujos

Mandar el mensaje y leer la tabla no se hace con el rol de lectura, sino con un rol aparte, FinOpsSentinelMonitoringFlow, que se crea con una plantilla de CloudFormation desde el acceso de la cuenta. Ese rol solo toca los recursos que tú pones en sus parámetros, y el asistente te dice cuáles necesita este flujo en «Permisos que necesita tu rol de flujos»:

Parámetros del stack FinOpsSentinelMonitoringFlow

TriggerQueueArns
arn:aws:sqs:eu-west-1:123456789012:orders-queue
ObservedTableArns
arn:aws:dynamodb:eu-west-1:123456789012:table/orders
  • Para ampliarlo: abre el stack en CloudFormation, pulsa «Actualizar», elige «Usar la plantilla actual», añade los ARN a los que ya tenga cada parámetro, separados por comas, y sigue hasta «Enviar». No sustituyas los que hay o dejarán de funcionar otros flujos.
  • Si cifras la cola con una clave KMS propia, añade también su ARN en KmsKeyArns.
  • En cada ejecución, además, la sesión se recorta a lo que hace este flujo: sqs:SendMessage sobre orders-queue y dynamodb:GetItem sobre orders, aunque el rol permita más.
  • Los logs se leen con el acceso de lectura de Monitorización, que es donde la cuenta concede logs:FilterLogEvents.

Cómo se lee el resultado

La ficha del flujo enseña el recorrido de la última prueba, con su traceId, y una fila de puntos por cada ejecución reciente. Cada paso sale como «Llegó a tiempo», «Llegó tarde», «No llegó» o «No se pudo comprobar», con el tiempo que tardó desde el anterior. Si un paso falla, se marca «Aquí se rompe» y los siguientes quedan «Sin comprobar: la cadena se rompió antes». Tres resultados típicos en este pipeline:

  • Paso 1 a tiempo (+3 s) y paso 2 a tiempo (+1 s): la cadena funciona de punta a punta.
  • Paso 1 a tiempo y paso 2 «No llegó»: la función recibió el pedido y no lo escribió. Es el caso del ítem que nunca se escribe, el que ninguna métrica ve. Mira la variable de entorno de la tabla y las excepciones capturadas.
  • Paso 1 «No llegó»: el mensaje no llegó a la función o la función no escribió la línea. Mira el monitor de orders-queue (mensajes acumulados, DLQ) y el de process-order (errores, throttling).
  • «No se pudo comprobar» no es lo mismo que «No llegó»: significa que no pudimos mirar, por ejemplo porque el rol no tiene permiso sobre la tabla. En ese caso la ficha enseña qué ARN falta en cada parámetro.

El tiempo de una línea de log es el de la propia línea, así que es exacto, y si aparece después de su plazo el paso sale como «Llegó tarde». Un ítem de DynamoDB no trae hora: su tiempo es el de la mirada que lo encontró, y las miradas se reparten por el plazo, unas seis por paso. El paso de logs espera además un minuto tras su plazo, lo que tarda CloudWatch en indexar una línea.

Cuándo avisa: aprendizaje y confirmación

Un monitor nuevo pasa sus primeras 24 horas aprendiendo: observa, enseña resultados y no avisa. Después, el estado pasa por sospechoso, confirmando y caído, y solo «caído» abre un incidente y avisa por correo, Slack, Microsoft Teams o webhook, los mismos canales que las alertas de coste. El incidente se cierra solo cuando el recurso o el flujo se recupera.

  • Monitores de recurso: dos lecturas malas seguidas declaran la caída y una buena la cierra. Se leen cada 10 minutos por defecto, cada 5 como mínimo.
  • Flujos: tres pruebas fallidas seguidas declaran la caída y dos correctas la cierran. Con pruebas cada 15 minutos, el aviso llega entre 30 y 45 minutos después de que se rompa la cadena; una prueba que falla una vez no despierta a nadie.
  • La confirmación desde una segunda región (las sondas salen de eu-west-1 y confirman desde eu-central-1) es de los monitores HTTP, donde descarta un problema de red. En un recurso o un flujo, repetir desde otra región leería las mismas métricas de la misma cuenta, así que ahí confirman los resultados seguidos. Si tu pipeline tiene una API delante que escribe en orders-queue, un monitor HTTP sobre ella sí se confirma desde la segunda región.

Cuánto cuesta

Los monitores de recurso y los flujos van en el paquete de Monitoring: 49 € al mes en los planes Pro y Business (39 € desde el cuarto paquete), con 25 monitores, 3 flujos, una página de estado y 90 días de historia. Este pipeline usa tres monitores y un flujo de un solo paquete. Las sondas corren en nuestra cuenta; en tu factura de AWS aparece esto:

  • La lectura de métricas con GetMetricData: hasta unos 0,20 USD por recurso al mes leyendo cada 10 minutos, así que como mucho unos 0,60 USD por los tres. Leer la DLQ son llamadas a SQS que caben de sobra en su capa gratuita de un millón de peticiones al mes.
  • Las pruebas del flujo: cada 15 minutos son unas 2.900 al mes (si el flujo agota su tope de coste del mes, por una cadena rota o muy larga y lenta, pasa a una cada 6 horas hasta el día 1, y a ese ritmo una incidencia tarda más en cerrarse), y cada una es un mensaje SQS, una invocación de process-order, una escritura en orders y unas veinte lecturas (GetItem y FilterLogEvents). Es una fracción muy pequeña de lo que mueve el tráfico real, pero cuenta con ella si tus precios son ajustados.

Lo que no hace

Para que sepas dónde están los límites:

  • Los flujos son solo de AWS. Los monitores de recurso sí cubren también Azure y Google Cloud.
  • No es un APM: no recoge tus logs, no hace trazas distribuidas ni métricas propias. Busca el traceId de la prueba en tus logs y en tu tabla, y lee las métricas que AWS ya publica.
  • El paso de DynamoDB necesita el traceId en la clave primaria, de texto, en una o dos claves. Si no puedes darle esa forma al ítem de prueba, un paso de callback o un objeto de S3 son las alternativas.
  • El descubrimiento sigue colas, funciones y sus destinos; si la cadena pasa por SNS, un bus de eventos o Step Functions, lo que hay detrás se añade a mano.

Checklist para empezar

Con estas piezas, el próximo pedido perdido te lo cuenta AAA Sentinel y no un cliente.

  • La cuenta de AWS conectada con el acceso de lectura de Monitoring.
  • Un monitor de recurso en orders-queue (cubre orders-dlq), otro en process-order y otro en orders.
  • La función reconoce la prueba por su traceId, escribe la línea y guarda el ítem con orderId = aaa-test-{traceId}, sin efectos reales.
  • TTL activado en la tabla para los ítems de prueba.
  • El rol de flujos con orders-queue en TriggerQueueArns y orders en ObservedTableArns.
  • El flujo creado con dos pasos y plazos de 60 y 120 segundos.
  • Al menos un canal de alerta probado antes de que acaben las 24 horas de aprendizaje.