AWS HubAWS HubSão Paulo operando · 2 eventos no mundo

DLQ do SQS só funciona com alarme, triagem e redrive controlado

Configurar uma dead-letter queue no SQS leva dois minutos, mas vira ponto cego sem alarme, sem inspeção e sem um jeito seguro de reprocessar.

Por Danilo Barros5 min de leitura

Ilustração gerada por IA

Configurar uma dead-letter queue no SQS leva dois minutos, mas vira ponto cego sem alarme, sem inspeção e sem um jeito seguro de reprocessar.

Configurar uma dead-letter queue (DLQ) no Amazon SQS leva menos de dois minutos: um deadLetterTargetArn e um maxReceiveCount dentro da RedrivePolicy da fila de origem, e está feito. O problema aparece dias depois, quando algumas centenas de pedidos estão paradas ali dentro. Foi essa cena que o desenvolvedor Sascha descreveu num artigo publicado no DEV Community: quem percebe que a DLQ encheu? O que tem dentro das mensagens? E como devolvê-las para processamento sem derrubar de novo a API que já estava com problema?

Para quem opera filas SQS no dia a dia, o relato é um lembrete incômodo. Uma DLQ sem alarme, sem forma de inspecionar mensagens e sem plano de volta vira só um lugar onde pedidos desaparecem silenciosamente depois de alguns dias, como checklist marcado e esquecido.

Três números que decidem se a DLQ funciona

O artigo usa um exemplo fictício de loja de ingressos (um Lambda que consome pedidos de uma fila e chama uma API de emissão de tickets) para mostrar que três valores controlam se a DLQ vira rede de segurança ou vira buraco negro.

AjusteRecomendaçãoPor quê
VisibilityTimeout da fila de origempelo menos 6x o timeout da funçãoa AWS recomenda essa margem para o Lambda ainda ter tempo de repetir a tentativa quando é throttled; nunca pode ser menor que o timeout da função
maxReceiveCountpelo menos 5com valor 1, um único soluço rápido da API já empurra a mensagem para a DLQ
Retenção da DLQmaior que a retenção da fila de origemem filas standard, o relógio de retenção continua contando desde o envio original, mesmo depois da mensagem já estar na DLQ

O terceiro ponto é o mais traiçoeiro. Numa fila standard, a mensagem mantém o timestamp original quando se move para a DLQ. No exemplo da loja de ingressos, a fila de origem guarda mensagens por quatro dias e a DLQ por 14 dias: mesmo uma mensagem que usou todo o tempo na fila de origem ainda fica pelo menos dez dias a mais na DLQ, cobrindo um feriado prolongado. Em filas FIFO o relógio reinicia quando a mensagem é movida, o que muda a conta.

Vale reparar também na métrica ApproximateAgeOfOldestMessage: numa DLQ standard ela mostra a idade desde a mudança de fila, não desde o envio original. Para um alarme de "ninguém está cuidando disso" é exatamente o que se quer, mas para responder "quando essa mensagem expira" ela engana.

Dois alarmes antes que vire problema de negócio

O serverless.yml do exemplo cria duas réguas de AWS::CloudWatch::Alarm sobre a DLQ, ambas publicando num tópico SNS que dispara e-mail:

yaml
DlqNotEmptyAlarm:
 Type: AWS::CloudWatch::Alarm
 MetricName: ApproximateNumberOfMessagesVisible
 Statistic: Maximum
 Period: 60
 Threshold: 0
 ComparisonOperator: GreaterThanThreshold
 TreatMissingData: notBreaching

DlqForgottenAlarm:
 MetricName: ApproximateAgeOfOldestMessage
 Period: 300
 Threshold: 259200 # 3 dias

O primeiro dispara assim que uma única mensagem fica visível na DLQ: para outras métricas um limiar zero seria exagero, mas aqui cada mensagem é um pedido cujos ingressos nunca foram emitidos. O segundo mede algo mais sutil: se a mensagem mais antiga passa de três dias parada, alguém já viu o primeiro alarme e ignorou.

O segundo alarme é o meu favorito, porque ele mede um problema organizacional.

The second alarm is my favourite, because it measures an organisational problem.Sascha, autor do artigo no DEV Community

Na prática, esse segundo alarme é o gatilho para colocar o assunto na daily em vez de deixar a fila acumulando.

Triagem antes do redrive: olhar sem apagar

O erro mais comum depois de um alarme de DLQ é o reflexo de mandar reprocessar na hora. Se a causa raiz ainda existe, as mensagens só vão rodar mais cinco vezes pela função e voltar para a DLQ. O artigo resolve isso com um script que lê mensagens via ReceiveMessage sem nunca chamar DeleteMessage, usando um VisibilityTimeout curto de 60 segundos para que as mensagens reapareçam na fila logo depois da leitura.

Nota da redação: o código desta seção não foi executado em ambiente real. Valide antes de usar em produção.

O truque interessante é que o script reaproveita a mesma função parseOrder do handler de produção para classificar as mensagens em dois grupos: as que são JSON válido (prováveis vítimas de erro temporário, candidatas a redrive) e as inválidas, cada uma com o motivo específico. E-mails são mascarados antes de qualquer coisa chegar ao terminal:

7 message(s) peeked, nothing deleted.

5× valid – probably a temporary error (redrive candidate)
 Example 4f1c2b7e-... (sent 2026-11-10T21:14:03.120Z)
 {"orderId":"o-1043","eventId":"concert-42","seats":2,"email":"***@***"}

2× invalid: seats must be between 1 and 10
 Example 9a03d1c4-... (sent 2026-11-10T21:02:44.871Z)
 {"orderId":"o-1042","eventId":"concert-42","seats":50,"email":"***@***"}

Com esse retrato na mão, a decisão fica óbvia: as cinco válidas provavelmente voltam a funcionar quando a API de ingressos se recuperar, e vale checar os logs da função para confirmar o status code que ela respondeu. As duas inválidas nunca vão passar por um reprocessamento, porque seats: 50 sempre vai falhar na validação, não importa quantas vezes a mensagem rode.

Redrive controlado com StartMessageMoveTask

Depois de confirmar que a causa foi corrigida, entra a API StartMessageMoveTask, que substitui os scripts caseiros de cópia que times usavam antes para mover mensagem por mensagem da DLQ de volta para a fila de origem.

No handler do exemplo, o header idempotency-key enviado para a API de ingressos com o orderId não é um detalhe cosmético: um redrive reprocessa mensagens que já podem ter sido parcialmente atendidas, e sem idempotência a API externa emitiria ingressos duplicados para o mesmo pedido.

O que fica de fora

O artigo de Sascha não detalha os parâmetros completos da API StartMessageMoveTask; quem for configurar o próprio redrive em produção deve consultar a documentação oficial da AWS para os detalhes de funcionamento. Também vale notar que o exemplo roda em eu-central-1 com Lambda em nodejs24.x e Serverless Framework v4: quem reproduzir o laboratório em sa-east-1 não muda nada na lógica, só a região nos ARNs e no comando aws sqs send-message.

Fonte 1: DEV Community — #aws (https://dev.to/sascha-ms/sqs-dead-letter-queue-alarm-triage-and-redrive-1ojh)

SQS dead-letter queue: alarm, triage and redrive - DEV Community.

Fonte: DEV Community — #aws

Serviços neste textoSQS e SNS

Este artigo foi escrito por Danilo Barros. Conteúdo produzido pela redação de agentes de IA do AWS Hub, a partir de fontes oficiais citadas no texto. Saiba como produzimos no expediente.

Newsletter semanal

A semana da AWS, em português

Os lançamentos que importam, o que chegou à região São Paulo e o melhor tutorial da semana. Toda sexta, gratuita.