Como implementar visibility timeout no SQS em tarefas demoradas: guia prático para produção
Aprenda a configurar, renovar e monitorar o visibility timeout no Amazon SQS para processar jobs longos sem reentregas perigosas, duplicidade e perda de trabalho.
Max Alex

Neste artigo (9 tópicos)
O visibility timeout SQS é um dos controles mais importantes para executar tarefas demoradas com segurança no Amazon SQS. Quando ele é menor que a duração real de um job, a mesma mensagem pode reaparecer na fila e ser consumida por outro worker antes de a primeira execução terminar.
O resultado costuma ser caro: conversões duplicadas, chamadas repetidas a APIs, cobranças indevidas, concorrência inesperada e filas cada vez mais difíceis de operar. Neste guia, você verá como dimensionar o timeout inicial, renová-lo durante o trabalho e construir um fluxo pronto para produção.
O que é visibility timeout no Amazon SQS
Depois que um consumidor recebe uma mensagem com ReceiveMessage, o SQS a mantém temporariamente invisível para os demais consumidores. Esse intervalo é o visibility timeout.
Durante esse período, o worker deve processar o job e confirmar o sucesso com DeleteMessage. A exclusão é a confirmação real do processamento. Se ela não acontecer antes do prazo, a mensagem volta a ficar visível e pode ser entregue novamente.
Isso torna o SQS adequado para processamento assíncrono, mas não transforma a invisibilidade em um lock perfeito. Uma falha de processo, rede ou confirmação pode provocar uma nova entrega mesmo quando o primeiro worker já executou parte do trabalho.
Imagine uma mensagem que pede a geração de um relatório. Enquanto o worker trabalha, ela fica invisível. Ao terminar de persistir o arquivo e o status do relatório, o worker remove a mensagem. Se o worker cair antes disso, outro consumidor poderá retomá-la mais tarde.
Por que tarefas demoradas falham com o timeout padrão
O erro mais comum é definir um timeout curto e assumir que ele protege todo o processamento. Uma conversão de vídeo que dura 12 minutos, por exemplo, não estará protegida por um timeout de 30 segundos.
Quando os 30 segundos expiram, outro worker pode receber a mesma mensagem e começar a mesma conversão. As duas execuções passam a competir por CPU, arquivos, banco de dados e serviços externos. Em operações não idempotentes, o efeito pode chegar ao cliente.
É importante considerar que o SQS trabalha com entrega pelo menos uma vez. Portanto, operações idempotentes continuam necessárias, mesmo com um timeout bem calculado.
Também não basta aumentar indiscriminadamente o timeout da fila. Um prazo muito longo reduz a velocidade de recuperação quando um worker realmente falha: a mensagem só poderá ser reprocessada quando a invisibilidade terminar. O objetivo é equilibrar proteção contra duplicidade e recuperação de falhas.
Em sistemas distribuídos, mensagens duplicadas em filas devem ser tratadas como uma condição normal de operação, não como uma exceção impossível.
Como escolher o visibility timeout inicial
Baseie o intervalo na distribuição real da duração dos jobs, e não na média. A média esconde picos: se a maioria das tarefas termina em 20 segundos, mas uma parcela relevante leva 90 segundos, um timeout de 30 segundos ainda causará reentregas.
Uma regra prática é começar pelo p99 do processamento e adicionar margem para I/O, retries locais, persistência do resultado, encerramento do worker e a chamada de exclusão. Se o p99 é 90 segundos e a margem operacional é 20 segundos, comece com 120 segundos e acompanhe o comportamento em produção.
- Jobs curtos e previsíveis: timeout próximo ao p99 com margem moderada.
- Jobs longos e variáveis: timeout inicial conservador mais renovação periódica.
- Jobs que podem ultrapassar horas: divida o trabalho em etapas quando possível e trate a renovação como requisito.
O SQS permite configurar visibility timeout de até 12 horas. Esse teto não deve ser a configuração padrão: ele é um limite técnico, não uma recomendação operacional.
Instrumente duração, falhas e reentregas para obter as métricas dos workers que sustentam esse cálculo. O timeout também deve respeitar os SLOs para processamento assíncrono: uma configuração que evita duplicidade, mas deixa o backlog crescer, ainda precisa ser revista.
Renove o timeout durante o processamento com ChangeMessageVisibility
Para tarefas cujo tempo é variável ou difícil de prever, use ChangeMessageVisibility como um heartbeat do processamento. A operação atualiza o tempo de invisibilidade da mensagem recebida usando o ReceiptHandle daquela entrega.
Renove antes de o prazo atual vencer. Um padrão simples é escolher um timeout de cinco minutos e tentar renová-lo a cada um ou dois minutos, com margem para falhas transitórias e latência de rede. Não espere os últimos segundos.
async function extendVisibility(sqs, queueUrl, receiptHandle) {
await sqs.changeMessageVisibility({
QueueUrl: queueUrl,
ReceiptHandle: receiptHandle,
VisibilityTimeout: 300
});
}Inicie a rotina de renovação após validar a mensagem e cancele-a imediatamente quando o processamento terminar, falhar de forma definitiva ou for cancelado. Se uma renovação falhar, registre o evento com o identificador do job e trate a mensagem como potencialmente reentregue; não presuma exclusividade.
Evite renovar apenas ao fim de uma etapa muito longa. Se cada etapa leva dois minutos, mas o timeout vence em cinco minutos, uma falha ou lentidão entre etapas pode criar uma janela de risco. Um timer independente e supervisionado costuma ser mais confiável.
Implemente um worker resiliente: receber, processar, renovar e excluir
O ciclo seguro começa antes do trabalho caro. Valide o schema e os campos obrigatórios do payload, gere ou propague um ID de correlação e registre o estado de execução em uma fonte durável quando o processo tiver efeitos relevantes.
- Receba a mensagem e capture o
ReceiptHandleatual. - Valide o payload e verifique se o job já foi concluído.
- Inicie a renovação periódica da invisibilidade.
- Execute o trabalho e persista o resultado ou estado final.
- Interrompa a renovação e chame
DeleteMessagesomente após o sucesso durável.
const renewal = setInterval(() => {
extendVisibility(sqs, queueUrl, message.ReceiptHandle)
.catch(error => logger.error({ error, jobId }, 'Falha ao renovar timeout'));
}, 60_000);
try {
await processJob(payload, jobId);
await saveCompletedState(jobId);
await sqs.deleteMessage({
QueueUrl: queueUrl,
ReceiptHandle: message.ReceiptHandle
});
} finally {
clearInterval(renewal);
}O exemplo mostra a ordem essencial, mas a implementação real precisa evitar que uma renovação continue após a exclusão e precisa observar falhas do timer. Em jobs críticos, mantenha o estado do trabalho separado da confirmação da mensagem. Assim, uma nova entrega consulta o estado persistido antes de repetir efeitos externos.
Nunca use o SQS como único mecanismo de deduplicação. O receipt handle identifica uma entrega, não uma garantia de que apenas um processo executará o efeito de negócio.
Projete para reentregas com idempotência e controle de concorrência
Mesmo com renovação correta, reentregas são possíveis. Um worker pode concluir uma chamada externa, perder conectividade antes do DeleteMessage e ter a mensagem entregue de novo. O projeto deve suportar esse cenário sem duplicar o resultado.
Use uma chave idempotente persistida, normalmente derivada do ID do job, pedido ou evento de origem. Antes de cobrar, enviar e-mail, criar recurso externo ou publicar uma alteração, verifique se aquela operação já foi concluída.
Uma abordagem comum é gravar uma linha de operação com unicidade no banco. O worker tenta criar ou reservar a operação; se encontrar um estado concluído, apenas confirma a mensagem. Se encontrar uma operação em andamento, aplica uma regra de retomada ou espera controlada.
Para efeitos externos, envie a mesma chave de idempotência ao provedor quando a API suportar esse mecanismo. Para recursos compartilhados, use transações, restrições únicas ou locks de negócio com expiração segura. Locks não substituem idempotência: eles reduzem concorrência, enquanto a idempotência protege o resultado.
Trate falhas com retries, DLQ e limites de recebimento
Falhas transitórias não devem levar à exclusão da mensagem. Se uma dependência estiver indisponível, deixe a mensagem retornar à fila ou ajuste sua invisibilidade para aplicar uma espera apropriada. Registre o motivo, a contagem de tentativas e o ID de correlação, sem expor dados sensíveis nos logs.
Configure uma dead letter queue SQS e uma política de redrive com maxReceiveCount compatível com o tipo de trabalho. Mensagens inválidas, esquemas incompatíveis e erros persistentes não devem circular indefinidamente na fila principal.
Escolha o limite considerando o custo e a possibilidade de recuperação. Um payload estruturalmente inválido pode ir para análise após poucas tentativas; uma falha temporária de serviço pode justificar mais tentativas, espaçadas por backoff.
Quando identificar uma falha definitiva, você pode reduzir a visibilidade para liberar a mensagem mais cedo, desde que isso seja coerente com a estratégia de retry. O ponto central é não mascarar uma mensagem problemática com renovações sem fim.
Monitore sinais de timeout inadequado em produção
Uma configuração saudável precisa ser comprovada por observabilidade. Acompanhe a idade aproximada da mensagem mais antiga, o volume de mensagens visíveis e em processamento, a profundidade da fila e a taxa de recebimentos repetidos.
Correlacione a duração de cada job com o número de renovações do visibility timeout. Crescimento inesperado nas extensões, mensagens recebidas muitas vezes ou falhas de DeleteMessage são sinais de que o timeout, a capacidade dos workers ou a lógica de processamento precisam de ajuste.
- Alerta para idade da mensagem mais antiga acima do SLO.
- Alerta para aumento de recebimentos repetidos e mensagens enviadas à DLQ.
- Dashboard com duração por percentil, taxa de sucesso e capacidade disponível.
- Logs estruturados com job ID, message ID, tentativa e resultado da renovação.
Teste cenários adversos antes de depender deles em incidente: mate um worker no meio do job, simule rede instável, atrase uma chamada externa e execute uma tarefa acima do p99. Verifique se a mensagem reaparece, se o estado é retomado e se nenhum efeito de negócio é duplicado.
Checklist de produção para visibility timeout no SQS
- O timeout inicial foi definido a partir de percentis de duração e margem operacional.
- Jobs longos renovam a invisibilidade antes do vencimento, usando o receipt handle atual.
- O worker para de renovar após sucesso, cancelamento ou falha definitiva.
- A mensagem só é excluída depois que o resultado está persistido de forma durável.
- Efeitos externos possuem chave idempotente e controle de concorrência adequado.
- A fila possui política de redrive, DLQ e procedimento para analisar mensagens isoladas.
- Há alertas para backlog, idade de mensagens, reentregas e falhas de exclusão.
- O time testou interrupção de worker e reprocessamento sem efeitos duplicados.
O visibility timeout SQS não elimina a necessidade de projetar para falhas; ele cria a janela de trabalho na qual um consumidor pode concluir sua tarefa com menos disputa. Em produção, a combinação correta é timeout dimensionado, renovação antecipada, idempotência, DLQ e métricas acionáveis.
Sua fila processa tarefas longas ou apresenta reentregas inesperadas? Estruture o fluxo com timeout dinâmico, idempotência e observabilidade antes que duplicidades afetem clientes e operações.
Max Alex
Desenvolvedor full stack especializado em SaaS, Laravel, IA aplicada, WhatsApp API, automações e integrações. Criação de sistemas web escaláveis para empresas.
Continue lendo

OpenTelemetry em microserviços: erros que geram falhas, lentidão ou retrabalho
Traces incompletos, overhead e perda de telemetria podem ter causas diferentes. Entenda oito erros de OpenTelemetry em microserviços e use um checklist para diagnosticar, corrigir e validar a instrumentação em produção.

GitHub Actions em monorepos: erros que geram falhas, lentidão ou retrabalho
Seu CI executa projetos demais, ignora dependências ou bloqueia merges? Conheça dez erros de GitHub Actions em monorepos e como verificar filtros, checks, cache, matrizes e permissões.

reverse proxy em APIs backend: erros que geram falhas, lentidão ou retrabalho
Erros de rotas, headers, timeouts e conexões no Nginx podem causar falhas e lentidão em APIs. Veja como localizar a camada responsável, corrigir a configuração e validar o resultado antes de publicar.