Monitoramento18 de outubro de 2026 11 min de leitura

Como monitorizar um pipeline SQS → Lambda → DynamoDB sem escrever código

Uma fila, uma função e uma tabela podem falhar de cinco maneiras diferentes, e algumas não deixam rasto nas métricas. Mostramos que sinal denuncia cada falha, como vigiar os três recursos com o AAA Sentinel sem mexer no código e como um fluxo de teste com traceId indica o passo exato em que a cadeia quebra.

Este artigo também está disponível em:esenfr

Uma encomenda entra numa fila SQS, uma função Lambda processa-a e o resultado fica guardado no DynamoDB. É um dos padrões mais comuns na AWS, e também um dos que falham em silêncio: a fila aceita mensagens mesmo que ninguém as leia, a função tenta de novo até alguma coisa correr bem e a tabela nunca se queixa do que não lhe chegou. Neste artigo vemos como esta cadeia falha, que sinal denuncia cada falha e como vigiá-la com o AAA Sentinel passo a passo, com nomes de exemplo que pode trocar pelos seus.

O pipeline do exemplo

Usamos uma loja fictícia com este pipeline na conta 123456789012, região eu-west-1:

Pipeline de exemplo

Fila de entrada
orders-queue, com redrive para a DLQ após 5 receções
Fila de mensagens falhadas (DLQ)
orders-dlq
Função
process-order, ligada à fila por um event source mapping
Tabela
orders, com chave de partição orderId (texto)

Como falha um pipeline assim

Quase todos os incidentes desta cadeia cabem num destes cinco casos:

  • As mensagens acumulam-se: a função está parada, o event source mapping está desativado ou a concorrência reservada ficou a zero. A fila continua a aceitar encomendas e ninguém as processa.
  • Mensagens envenenadas: uma mensagem com um formato que a função não entende falha em cada tentativa. Esgotado o tempo de visibilidade, volta à fila, e ao atingir o máximo de receções da redrive policy acaba na DLQ. Se a função processa lotes sem reportar as falhas por mensagem (ReportBatchItemFailures), uma única mensagem má faz repetir o lote inteiro.
  • Erros e limitações da função: exceções, tempos esgotados ou throttling por falta de concorrência. Um pico de erros costuma notar-se; 3 % constantes, não.
  • Tentativas que escondem a falha: a mensagem falha duas vezes e à terceira corre bem. Não chega à DLQ e a taxa de erros não chama a atenção, mas a encomenda fica guardada com minutos de atraso.
  • Itens que nunca são escritos: a função termina bem, mas não escreve. Uma variável de ambiente que aponta para outra tabela, uma escrita condicional descartada, uma exceção apanhada que só deixa uma linha no log. Nenhuma métrica se mexe.

Que sinal denuncia cada falha

O CloudWatch já publica quase tudo o que é preciso. Este é o sinal que melhor deteta cada caso:

Falha → sinal

Mensagens acumuladas
SQS ApproximateAgeOfOldestMessage da orders-queue. Melhor do que contar mensagens: o número sobe com a carga, a antiguidade sobe quando ninguém consome
Mensagens envenenadas
ApproximateNumberOfMessagesVisible da orders-dlq acima de zero. Uma mensagem ali já esgotou todas as tentativas
Erros da função
Lambda Errors face a Invocations da process-order, em percentagem
Limitações da função
Lambda Throttles da process-order
Função lenta
Lambda Duration p95 perto do timeout configurado: quando o atingir, cada tentativa passa a ser um erro
Tabela saturada
DynamoDB ReadThrottleEvents e WriteThrottleEvents da orders
Itens não escritos e tentativas que atrasam
Nenhuma métrica: só se vê enviando um teste e verificando que chega ao fim

As métricas dizem que cada peça está ligada, não que a cadeia funciona. Uma fila vazia, uma função sem erros e uma tabela sem throttling são compatíveis com zero encomendas guardadas.

Passo 1: vigiar a fila, a DLQ, a função e a tabela

Esta parte não precisa de código. Em Monitorização → Novo → Monitor, escolha o tipo Recurso cloud, a conta já ligada, a região e o recurso da lista (ou cole o ARN). Se a conta ainda não concede o acesso de leitura de Monitorização, o formulário indica-o e oferece «Conceder a permissão». O AAA Sentinel lê as métricas com essa função de só leitura, sem agentes. Com três monitores ficam cobertos os quatro recursos:

  • orders-queue (Fila SQS): dá-se por em baixo se houver alguma mensagem na fila de mensagens falhadas ou se a mensagem mais antiga estiver há mais tempo do que a antiguidade máxima sem ser consumida (60 minutos se o campo ficar vazio). A DLQ não precisa de monitor próprio: o monitor da fila encontra-a na redrive policy e lê o número de mensagens diretamente do SQS, sem esperar pelo CloudWatch.
  • process-order (Função Lambda): dá-se por em baixo se falharem ou forem limitadas 10 % ou mais das execuções (com pelo menos 10), ou se falharem todas havendo entre 3 e 9. A percentagem ajusta-se em «Percentagem de falhas (%)».
  • orders (Tabela DynamoDB): dá-se por em baixo se rejeitar por capacidade mais de 10 pedidos no período, somando leituras e escritas. Ajusta-se em «Rejeições por capacidade toleradas»; numa tabela on-demand com pouco tráfego pode baixar-se para 0.

O monitor da função olha para erros, limitações e, em origens do tipo stream, para a antiguidade do iterador; não olha para a duração. Se a p95 de Duration importa por si só, um alarme do CloudWatch é a ferramenta certa. Para o que o cliente sente, o tempo de ponta a ponta, existe o fluxo do passo seguinte.

Passo 2: o que faz um fluxo

Um fluxo verifica a cadeia inteira com uma transação de teste. No máximo a cada 15 minutos, o AAA Sentinel envia uma mensagem para a orders-queue com um traceId próprio (32 caracteres hexadecimais) e verifica que chega a cada passo dentro do prazo. A mensagem leva o traceId no atributo de mensagem x-aaa-trace e no corpo como aaaTraceId, junto de um traceparent W3C; numa fila FIFO, o teste vai no seu próprio grupo de mensagens. Os passos possíveis são uma linha no CloudWatch Logs, um item do DynamoDB, um objeto do S3 ou um callback que o código faz para um URL nosso, até cinco. Para este pipeline usamos dois: a linha no log da process-order e o item na orders.

Passo 3: preparar a função (aqui sim, umas linhas)

Para sermos honestos com o título: vigiar os recursos não exige código, mas o fluxo precisa que a função reconheça o teste. O teste percorre a cadeia real, por isso o código tem de o tratar sem efeitos reais (sem cobranças, e-mails nem encomendas) e deixar duas marcas que possamos encontrar:

Node.js com @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')
      // Teste do AAA Sentinel: sem cobranças nem e-mails, e com o traceId na chave para o passo do DynamoDB o encontrar.
      await db.send(new PutCommand({
        TableName: process.env.ORDERS_TABLE,
        Item: {
          orderId: `aaa-test-${trace.traceId}`,
          ...trace.fields,
          // Atributo TTL da tabela: o DynamoDB apaga sozinho os itens de teste.
          expiresAt: Math.floor(Date.now() / 1000) + 86400,
        },
      }))
      continue
    }
    // Aqui segue a lógica real da encomenda.
  }
}
Python, sem dependênciaspython
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}))
            # Teste do AAA Sentinel: sem efeitos reais, e com o traceId na chave do item.
            table.put_item(Item={"orderId": f"aaa-test-{trace_id}", "aaaTraceId": trace_id, "expiresAt": int(time.time()) + 86400})
            continue
        # Aqui segue a lógica real da encomenda.
  • Uma linha no log com o traceId. Se a função já escreve no log o evento ou o corpo da mensagem, o traceId aparece lá e não é preciso mudar nada para este passo.
  • Um item cuja chave primária contenha o traceId. O passo do DynamoDB não procura por atributo: faz um GetItem com leitura consistente sobre a chave definida, com {traceId} substituído pelo de cada teste, e só pede os atributos da chave, não o conteúdo do item. Por isso a chave tem de ser de texto e incluir o traceId (aqui, orderId = aaa-test-{traceId}); guardá-lo só num atributo não serve, e os índices secundários também não são consultados.

O propagateTrace lê o traceId de qualquer evento (SQS, SNS, EventBridge, DynamoDB Streams, HTTP) e devolve os cabeçalhos, os atributos de mensagem e os campos para o passar ao passo seguinte; o trace.log escreve uma linha JSON que termina com o traceId. Ative o TTL da tabela sobre expiresAt para que os itens de teste não se acumulem.

Passo 4: criar o fluxo no assistente

Em Monitorização → Novo → Fluxo, o assistente tem três passos:

Fluxo de exemplo · valores

O que inicia o fluxo
Fila SQS · arn:aws:sqs:eu-west-1:123456789012:orders-queue
Conteúdo do teste
{"type": "order", "test": true}
Passo 1 · Linha nos logs
arn:aws:logs:eu-west-1:123456789012:log-group:/aws/lambda/process-order:* · texto {traceId} · prazo 60 s
Passo 2 · Item do DynamoDB
Tabela orders · orderId = aaa-test-{traceId} · prazo 120 s
Frequência
A cada 15 minutos
  • Onde começa a cadeia: escolha a conta, a região e, em «O que inicia o fluxo», Fila SQS; cole o ARN da orders-queue e carregue em «Descobrir a cadeia». Só se lê a configuração; ainda não é lançado nenhum teste. O AAA Sentinel segue o event source mapping até à process-order e propõe um passo de logs por função, com o texto {traceId} e um prazo de 60 segundos.
  • O que se verifica: reveja o passo de logs e acrescente o último em «Adicionar o último passo» → Item do DynamoDB. A lista de tabelas mostra primeiro as que aparecem na configuração da função («aparece na sua função»): se ORDERS_TABLE aponta para orders, ela surge no topo. Em «Chave do item», escreva orderId e aaa-test-{traceId}. Em «Conteúdo do teste» pode juntar o que o código precisar para não descartar a mensagem.
  • Nome e alertas: o nome, a severidade e a frequência do teste, no mínimo a cada 15 minutos, porque cada teste escreve na cadeia e faz cerca de vinte leituras na conta.

Cada prazo conta desde o disparo, não desde o passo anterior, vai de 10 a 900 segundos e não pode ser menor do que o do passo anterior. Com 60 e 120 segundos, a encomenda tem dois minutos para estar na tabela.

Passo 5: a função de fluxos

Enviar a mensagem e ler a tabela não se faz com a função de leitura, mas com uma função IAM à parte, FinOpsSentinelMonitoringFlow, criada com um modelo do CloudFormation a partir do acesso da conta. Essa função só mexe nos recursos indicados nos seus parâmetros, e o assistente mostra quais são precisos para este fluxo em «Permissões de que a sua função de fluxos precisa»:

Parâmetros do stack FinOpsSentinelMonitoringFlow

TriggerQueueArns
arn:aws:sqs:eu-west-1:123456789012:orders-queue
ObservedTableArns
arn:aws:dynamodb:eu-west-1:123456789012:table/orders
  • Para a alargar: abra o stack no CloudFormation, carregue em «Atualizar», escolha «Usar o modelo atual», acrescente os ARN aos que cada parâmetro já tem, separados por vírgulas, e avance até «Enviar». Não substitua os existentes, ou outros fluxos deixam de funcionar.
  • Se a fila estiver cifrada com uma chave KMS própria, acrescente também o ARN dela em KmsKeyArns.
  • Em cada execução, a sessão é ainda recortada ao que este fluxo faz: sqs:SendMessage sobre a orders-queue e dynamodb:GetItem sobre a orders, mesmo que a função IAM permita mais.
  • Os logs leem-se com o acesso de leitura de Monitorização, que é onde a conta concede logs:FilterLogEvents.

Como se lê o resultado

A ficha do fluxo mostra o percurso do último teste, com o traceId, e uma fila de pontos por cada execução recente. Cada passo aparece como «Chegou a tempo», «Chegou tarde», «Não chegou» ou «Não foi possível verificar», com o tempo que levou desde o anterior. Se um passo falha, fica marcado como o ponto onde a cadeia quebra e os seguintes ficam «Não verificado: a cadeia quebrou antes». Três resultados típicos neste pipeline:

  • Passo 1 a tempo (+3 s) e passo 2 a tempo (+1 s): a cadeia funciona de ponta a ponta.
  • Passo 1 a tempo e passo 2 «Não chegou»: a função recebeu a encomenda e não a escreveu. É o caso do item que nunca é escrito, o que nenhuma métrica vê. Verifique a variável de ambiente da tabela e as exceções apanhadas.
  • Passo 1 «Não chegou»: a mensagem não chegou à função, ou a função não escreveu a linha. Veja o monitor da orders-queue (mensagens acumuladas, DLQ) e o da process-order (erros, throttling).
  • «Não foi possível verificar» não é o mesmo que «Não chegou»: significa que não conseguimos olhar, por exemplo porque a função IAM não tem permissão sobre a tabela. Nesse caso a ficha mostra que ARN falta em cada parâmetro.

O tempo de uma linha de log é o da própria linha, por isso é exato, e se aparecer depois do prazo o passo fica «Chegou tarde». Um item do DynamoDB não traz hora: o seu tempo é o da verificação que o encontrou, e as verificações repartem-se pelo prazo, cerca de seis por passo. O passo de logs espera ainda um minuto depois do prazo, o que o CloudWatch pode demorar a indexar uma linha.

Quando avisa: aprendizagem e confirmação

Um monitor novo passa as primeiras 24 horas a aprender: observa, mostra resultados e não avisa. Depois, o estado passa por suspeito, a confirmar e em baixo, e só «em baixo» abre um incidente e avisa por e-mail, Slack, Microsoft Teams ou webhook, os mesmos canais dos alertas de custo. O incidente fecha-se sozinho quando o recurso ou o fluxo recupera.

  • Monitores de recurso: duas leituras más seguidas declaram a falha e uma boa fecha-a. Leem a cada 10 minutos por omissão, a cada 5 no mínimo.
  • Fluxos: três testes falhados seguidos declaram a falha e dois corretos fecham-na. Com testes a cada 15 minutos, o aviso chega entre 30 e 45 minutos depois de a cadeia quebrar; um teste que falha uma vez não acorda ninguém.
  • A confirmação a partir de uma segunda região (as sondas saem de eu-west-1 e confirmam a partir de eu-central-1) é dos monitores HTTP, onde exclui um problema de rede. Num recurso ou num fluxo, repetir a partir de outra região leria as mesmas métricas da mesma conta, por isso aí confirmam os resultados seguidos. Se o pipeline tiver uma API à frente que escreve na orders-queue, um monitor HTTP sobre ela já é confirmado a partir da segunda região.

Quanto custa

Os monitores de recurso e os fluxos fazem parte do pacote de Monitoring: 49 €/mês nos planos Pro e Business (39 € a partir do quarto pacote), com 25 monitores, 3 fluxos, uma página de estado e 90 dias de histórico. Este pipeline usa três monitores e um fluxo de um único pacote. As sondas correm na nossa conta; na fatura da AWS aparece isto:

  • A leitura de métricas com GetMetricData: até cerca de 0,20 USD por recurso por mês a ler a cada 10 minutos, ou seja, no máximo cerca de 0,60 USD pelos três. Ler a DLQ são chamadas ao SQS que cabem com folga no nível gratuito de um milhão de pedidos por mês.
  • Os testes do fluxo: a cada 15 minutos são cerca de 2.900 por mês (se o fluxo esgotar o teto de custo do mês, por uma cadeia avariada ou muito longa e lenta, passa a um a cada 6 horas até ao dia 1, e a esse ritmo um incidente demora mais a fechar), e cada um é uma mensagem SQS, uma invocação da process-order, uma escrita na orders e cerca de vinte leituras (GetItem e FilterLogEvents). É uma fração muito pequena do que o tráfego real movimenta, mas convém contar com ela se as margens forem apertadas.

O que não faz

Para que saiba onde estão os limites:

  • Os fluxos são só da AWS. Os monitores de recurso cobrem também o Azure e o Google Cloud.
  • Não é um APM: não recolhe os seus logs, nem faz tracing distribuído ou métricas próprias. Procura o traceId do teste nos logs e na tabela, e lê as métricas que a AWS já publica.
  • O passo do DynamoDB precisa do traceId na chave primária, de texto, numa ou duas chaves. Se não for possível dar essa forma ao item de teste, um passo de callback ou um objeto do S3 são as alternativas.
  • A descoberta segue filas, funções e os seus destinos; se a cadeia passar por SNS, um bus de eventos ou Step Functions, o que está por trás acrescenta-se à mão.

Checklist para começar

Com estas peças no lugar, a próxima encomenda perdida é o AAA Sentinel a avisar, não um cliente.

  • A conta AWS ligada com o acesso de leitura de Monitoring.
  • Um monitor de recurso na orders-queue (cobre a orders-dlq), outro na process-order e outro na orders.
  • A função reconhece o teste pelo traceId, escreve a linha e guarda o item com orderId = aaa-test-{traceId}, sem efeitos reais.
  • TTL ativado na tabela para os itens de teste.
  • A função de fluxos com a orders-queue em TriggerQueueArns e a orders em ObservedTableArns.
  • O fluxo criado com dois passos e prazos de 60 e 120 segundos.
  • Pelo menos um canal de alerta testado antes de acabarem as 24 horas de aprendizagem.