Detecção de tarefas travadas: heartbeats, timeouts e alertas
Aprenda detecção de tarefas travadas com heartbeats simples, timeouts sensatos e alertas claros para identificar se o trabalho está lento, falhou ou realmente travado.

Por que “não está rodando” é tão difícil de debugar
Quando alguém diz “não está rodando”, geralmente quer dizer “cliquei no botão e nada aconteceu.” O problema é que a maioria das aplicações faz muito trabalho depois do clique, fora da vista. Sem sinais claros, você não sabe se o trabalho nunca começou, começou e está apenas lento, ou começou e travou no meio.
Um job (tarefa em segundo plano) é simplesmente uma tarefa que sua app faz depois, sem fazer o usuário esperar na tela. Exemplos comuns: gerar um PDF, importar um CSV, enviar e-mails, construir um relatório ou sincronizar dados. O usuário dispara a ação, e a app entrega para um worker que roda em segundo plano.
Do lado de fora, três resultados diferentes podem parecer exatamente iguais:
- Lento: está rodando, mas demorando mais que o esperado.
- Falhou: parou com erro e não vai terminar sozinho.
- Travado: começou, mas não está progredindo mais.
Todos os três parecem “nada está acontecendo.” Seu painel pode continuar dizendo “queued” ou “processing” por horas. Mensagens de suporte se acumulam. Fundadores acabam chutando: o servidor caiu, vai terminar à noite, o usuário fez algo errado?
Detecção de jobs travados é como transformar esse chute numa resposta clara. Você quer uma forma simples de ver:
- O job começou?
- Ele ainda está vivo agora?
- Quando foi a última vez que progrediu?
- Quando declaramos que excedeu o timeout?
- Quem recebe alerta e o que deve fazer em seguida?
Quando essas perguntas ficam visíveis, “não está rodando” vira um problema diagnosticável, não um mistério.
Lento vs falhou vs travado: uma definição simples
Quando alguém diz “o job não está rodando”, geralmente significa uma de três coisas. Se você não nomear qual delas, times tendem a chutar, reexecutar e gerar trabalho duplicado (incluindo cobrar o cliente duas vezes ou enviar o mesmo e-mail em duplicidade).
Lento
Um job lento ainda avança. Pode estar fazendo upload de arquivos, chamando uma API ou processando registros, só que mais devagar do que você esperava. O sinal chave é que algo muda: timestamps são atualizados, números de progresso aumentam ou novas linhas de log aparecem.
Falhou
Um job falhou parou. Não vai terminar sozinho. Pode ter crashado, encontrado um erro irreversível ou perdido acesso a algo necessário (permissão ou arquivo faltando). O ponto é que terminou, e só se resolve corrigindo a causa e reexecutando.
Travado
Um job travado é o mais complicado. O job “existe” (está na fila ou marcado como running), mas não está mais progredindo. Está vivo o suficiente para bloquear outro trabalho, porém não saudável para terminar.
Na prática:
- Lento: o progresso continua mudando, mesmo que pouco.
- Falhou: termina com erro e para.
- Travado: parece rodando, mas nada muda por tempo demais.
Exemplo: o e-mail do relatório diário não saiu. Se você reexecutar às cegas, pode enviar duas vezes mais tarde. Se souber que falhou, pode reexecutar com segurança. Se souber que está travado, você pode reiniciá-lo de propósito em vez de esperar horas.
Os três blocos: heartbeats, timeouts, alertas
Se você quer que detecção de jobs travados pareça simples, pense nisso como rastrear uma entrega. Você precisa de prova de que está se movendo, um ponto onde decide que não está, e um jeito de avisar alguém.
1) Heartbeats: prova de vida (ou progresso)
Um heartbeat é um pequeno sinal que um job envia enquanto roda. Pode ser tão básico quanto “ainda estou trabalhando” a cada minuto, ou melhor: “processei 3 de 20 itens.” O objetivo não é detalhe — é confiança de que o job está se movendo.
Heartbeats bons são consistentes e baratos. Um job de exportar arquivo pode enviar heartbeat após cada chunk escrito. Uma sincronização de dados pode heartbeat após cada cliente atualizado. Se um job não consegue reportar progresso significativo, ao menos deve reportar “estou vivo” em um cronograma.
2) Timeouts: quando “talvez lento” vira “travado”
Um timeout é a regra que transforma silêncio em um estado claro. Responde: quanto tempo você aceita esperar sem heartbeat antes de tratar o job como travado?
Uma configuração simples:
- Escolha um intervalo esperado de heartbeat (por exemplo, a cada 60 segundos).
- Permita uma janela de tolerância (por exemplo, 5 heartbeats perdidos).
- Marque o job como travado quando essa janela for excedida.
- Registre o último progresso conhecido para saber onde parou.
3) Alertas: uma mensagem que um humano pode agir
Um alerta deve alcançar uma pessoa real e incluir contexto suficiente para responder sem chutar: qual job, qual cliente, quando foi o último progresso e o que o sistema fez a seguir (reitou, pausou ou falhou).
Você não precisa de um dashboard sofisticado. Uma visão de status simples basta: última execução, último heartbeat e se um timeout foi disparado. Isso transforma “não está rodando” em um problema observável em vez de um debate.
Como adicionar heartbeats sem superengenharia
Um heartbeat é só um pequeno sinal de “ainda vivo” que seu job atualiza enquanto roda. Ele transforma detecção de jobs travados de adivinhação para um fato verificável: quando o job reportou progresso pela última vez?
Comece com o menor sinal de progresso que você pode atualizar com segurança. Para a maioria dos times, uma única linha numa tabela do banco de dados é suficiente. Mantenha simples e durável para que um restart não apague o registro.
Escolha um heartbeat difícil de fingir
Escolha um campo que corresponda ao que “progresso” significa para o job:
last_heartbeat_at(timestamp)current_step(por exemplo: "fetching data", "writing file", "sending email")processed_count(quantos itens foram feitos)
Atualize o heartbeat somente depois de terminar uma unidade real de trabalho (uma página buscada, um lote escrito, uma fatura processada). Assim ele reflete movimento real, não um loop que gira para sempre.
Defina um ritmo de atualização que caiba no job
Um heartbeat que dispara com muita frequência gera ruído. Muito raro, e você descobre tarde.
Como guia: atualize por passo em jobs curtos, a cada 1 a 5 minutos para jobs longos, e após cada chamada externa para qualquer coisa que dependa de uma API, upload ou serviço de terceiros.
Torne isso visível. Uma pequena view administrativa basta: ID do job, status, passo atual e último heartbeat. Então “não está rodando” vira “parou de bater heartbeat às 14:14 enquanto escrevia o passo 3.”
Escolhendo timeouts que batem com a vida real
Um timeout não deve ser um número aleatório. É uma promessa: “se este job não progredir até X, algo provavelmente está errado, e vamos responder de forma previsível.”
Comece com dois timeouts, não só um:
- Um soft timeout: “está levando mais que o normal”
- Um hard timeout: “pare de esperar, algo está errado”
Baseie os padrões em execuções reais, não em suposições. Observe sua execução mais lenta normal (não o melhor dia, nem uma queda completa) e use isso como ponto de partida. Se relatórios geralmente terminam em 2–4 minutos, mas às vezes chegam a 8, um soft em 6 e um hard em 15 é mais útil do que um hard em 5 que dispara o tempo todo.
Alguns jobs são “trabalho rápido mais espera longa.” Esperar por uma API externa, upload de arquivo ou provedor de pagamento pode pausar o progresso sem significar que o job quebrou. Trate isso dando a cada passo seu próprio limite, assim você vê onde o tempo está sendo gasto.
Uma forma simples de definir timeouts
Use isto como ponto de partida e ajuste com dados reais:
- Soft timeout: 1.5× a 2× sua execução normal mais lenta
- Hard timeout: 3× a 5× sua execução normal mais lenta
- Timeouts por passo: defina por chamada externa ou espera, baseado no comportamento normal do serviço
Quando um timeout é atingido, decida o comportamento com antecedência. Opções simples: marcar como travado e alertar alguém, reexecutar se for seguro, ou parar e exigir revisão.
Motivos comuns para jobs travarem (sem jargão de código)
A maioria dos jobs travados não é misteriosa. Normalmente estão esperando algo, bloqueados por algo ou repetindo o mesmo passo sem avançar.
Quatro causas aparecem sempre:
- O worker para no meio. A máquina executando o job pode crashar, reiniciar, ficar sem memória ou perder rede. O job já começou, então parece “em progresso”, mas ninguém está fazendo o trabalho.
- Espera por outro serviço que não responde. Provedores de e-mail, gateways de pagamento, APIs de IA, armazenamento de arquivos, sistemas parceiros. Se o serviço fica pendente (sem erro limpo), seu job pode ficar lá para sempre a menos que você defina um limite.
- Algo bloqueia no banco de dados. Um job pode tentar atualizar um registro enquanto outro processo mantém um lock. Por fora parece ocioso; por dentro está num engarrafamento.
- O job entra em loop ou refaz o mesmo trabalho. Um bug pequeno pode causar retries infinitos, processamento duplicado ou um loop que nunca chega em “done”. Tecnicamente está rodando, mas não avança.
Um checape rápido: parou de progredir ou parou completamente? Se um job está marcado como running mas “rows processed” não mudou em 20 minutos, você está olhando para espera, bloqueio ou loop.
Alertas que ajudam alguém a agir
Um alerta só é útil se disser a uma pessoa real o que fazer em seguida. Se só disser “worker error” às 2 da manhã, as pessoas vão silenciá-lo e você perde o objetivo do monitoramento.
Comece com um canal que vocês já observam, como e-mail ou chat do time. Adicione escalonamento depois que confiar nos alertas.
Alerta apenas em estados sobre os quais alguém pode agir: um job travado (sem heartbeat por X minutos), um job que falhou várias vezes seguidas, ou a fila acumulando (por exemplo, o job mais antigo tem mais de 15 minutos). Evite alertas “FYI” como “job started” a menos que esteja em debug.
Cada alerta deve incluir contexto suficiente para decidir rápido:
- Nome do job e funcionalidade a que pertence
- Usuário ou conta afetada (se aplicável)
- Hora de início e último heartbeat
- Último passo conhecido (por exemplo: “Generating PDF”)
- Responsável e primeira ação
Torne a primeira ação explícita e entediante. Exemplo: “Responsável: Suporte. Primeira ação: reexecutar uma vez. Se falhar de novo, escalar para Engenharia com este ID do job.”
Passo a passo: torne jobs travados diagnosticáveis em um dia
Você pode implementar detecção de jobs travados em um dia se manter simples: defina o que “saudável” significa e depois meça.
Comece listando os poucos jobs em segundo plano que fariam estrago se parassem silenciosamente (sincronização de cobrança, relatórios noturnos, envio de e-mails, importações de arquivos). Para cada um, defina o que “feito” significa em termos simples: uma linha salva, um relatório entregue, um lote de e-mail concluído, um arquivo marcado como processado.
Em seguida, adicione um heartbeat. É só um timestamp que seu job atualiza enquanto trabalha. Atualize quando o job começa, ocasionalmente durante o progresso e mais uma vez no fim. Agora “não está rodando” vira “o último heartbeat foi há 23 minutos enquanto processava o passo 3.”
Depois defina uma regra de timeout por job. Baseie em heartbeats faltantes para trabalhos longos ou tempo máximo de execução para trabalhos curtos. Escolha algo realista baseado no comportamento normal, não no otimista.
Por fim, dê a si um único lugar para olhar. Quatro estados bastam:
- Running (heartbeat recente)
- Done (marcador de finalização gravado)
- Failed (erro registrado)
- Stuck (timeout disparado)
Prove que funciona
Teste forçando uma falha de propósito: pare o worker no meio do job ou simule um crash. Confirme duas coisas: o job aparece como travado, e o alerta inclui qual job é, quando começou e o último heartbeat.
Com isso no lugar, você não vai mais chutar. Vai observar.
Erros comuns e armadilhas a evitar
O objetivo é simples: quando algo para de se mover, você descobre rápido e sabe o que fazer. Times costumam falhar porque os sinais parecem “verdes” até que um cliente reclame.
Uma armadilha comum é tratar um único sinal de “started” como heartbeat. Se seu worker só reporta no começo, uma parada no meio pode parecer saudável por horas.
Timeouts viram problema quando definidos por chute. Se forem menores que execuções lentas normais (relatórios de fim de mês, grandes importações, pico de tráfego), você vai receber alertas de trabalho que teria terminado bem. Pessoas aprendem a ignorar alertas — aí o monitoramento perde a função.
Retries também geram problemas silenciosos. Se um job pode rodar duas vezes e causar uma segunda cobrança, um e-mail duplicado ou um reembolso duplo, retry automático transforma uma falha em um problema de suporte.
Alertas são difíceis de agir quando faltam básicos: qual cliente é afetado, que passo alcançou, quem é o dono e se alguém já reconheceu o alerta.
Um teste prático: se um alerta acorda alguém, essa pessoa deveria responder “o que quebrou, quem foi afetado e qual a próxima ação segura” em menos de dois minutos. Se não, o alerta não está bom.
Checklist rápido antes de liberar monitoramento
Antes de rodar, busque um desfecho: quando alguém diz “não está rodando”, você consegue responder o que parou, quando e o que fazer em seguida.
- Para qualquer job marcado como “running”, você consegue ver quando ele checou por último?
- Você tem uma regra simples para “travado”, escrita numa frase (ex.: “sem check-in por 10 minutos = travado”)?
- Quando um alerta dispara, ele diz qual job, qual cliente/workspace, quando progrediu por último e o último passo concluído?
- Se você reexecutar, está protegido contra duplicatas (cobrança dupla, envio do mesmo e-mail, criação de dois relatórios)?
- Em emergência, alguém pode pausar ou reexecutar o job sem tocar no código, e está claro quem pode fazer isso?
Um teste prático: peça a um colega não técnico ler um alerta e explicar o que faria em seguida. Se não conseguir, o alerta não está pronto.
Cenário exemplo: o relatório faltante que na verdade estava travado
Um fundador clica em “Generate report” para um cliente, fecha a aba e espera. Dez minutos depois, nada chega por e-mail nem aparece no app. A mensagem de suporte é curta: “Não está rodando.”
Com detecção de jobs travados, o dashboard conta uma história mais clara:
- Job ID #18422 começou às 10:03
- Passo atual: “Export to PDF”
- Último heartbeat: 18 minutos atrás
- Status: Stuck (esperado heartbeat a cada 60 segundos)
Um alerta útil chega no lugar certo e diz o essencial:
- Job de relatório está travado em “Export to PDF”
- Último progresso: 12.430 linhas processadas
- Conta afetada: Acme Co
- Ação segura: reexecutar a partir do passo de export (sem cobrança dupla, sem e-mails duplicados)
Agora a pessoa de plantão tem um caminho claro. Primeiro, reinicia o job com segurança, usando um retry que reutiliza o mesmo registro de saída em vez de criar um segundo relatório. O cliente recebe o relatório.
Depois, conserta a causa. Na prática, costuma ser uma requisição dentro do passo de export que não retorna, ou um lock no banco que congela a query até o worker desistir.
Da próxima vez é mais tranquilo porque o job está dividido em passos claros, e cada passo tem seu próprio timeout.
Próximos passos: torne seus jobs confiáveis e fáceis de suportar
Comece pequeno. Escolha o job em segundo plano que mais causa dor (relatórios perdidos, faturas falhadas, e-mails atrasados). Adicione heartbeats e uma regra de timeout a esse job primeiro. Você aprende mais com um job bem instrumentado do que com monitoramento raso em todo lugar.
Escreva em inglês claro o que “saudável” significa para que qualquer pessoa julgue num relance. Exemplo: “Este job deve atualizar seu heartbeat pelo menos uma vez por minuto e terminar dentro de 8 minutos. Se der timeout, deve alertar.” Essa frase vira sua definição compartilhada de normal.
Se sua base de código começou como um protótipo gerado por IA, presuma que há casos de borda escondidos, mesmo que na maior parte funcione. Falhas costumam aparecer como jobs travados porque um worker espera para sempre, falha silenciosamente ou reexecuta de forma que deixa os dados bagunçados.
Se você está lidando com um app gerado por IA que fica pendurado em background e quer um caminho rápido e prático para produção, FixMyMess (fixmymess.ai) foca em diagnosticar e reparar problemas como heartbeats faltantes, retries inseguros e lógica quebrada de workers para que seus jobs fiquem observáveis e suportáveis.
Quando o básico estiver estável, as próximas melhorias normalmente são: retries mais seguros (executáveis duas vezes sem problema), uma página de status simples para saúde dos jobs e checagens de deploy que detectam problemas de configuração antes de chegar à produção.
Perguntas Frequentes
What exactly is a background job?
Um job em segundo plano é um trabalho que sua aplicação executa depois que o usuário clica em algo, sem mantê-lo preso numa tela de carregamento. Exemplos comuns: gerar relatórios, importar CSVs, exportar PDFs e enviar e-mails.
Why does “it’s not running” feel so hard to debug?
Porque três situações diferentes podem parecer idênticas por fora: o job está lento, ele falhou ou está travado. Sem sinais como atualizações de progresso ou timestamps, não dá para saber com qual você está lidando.
How can I tell if a job is slow rather than stuck?
Lento significa que ele ainda está avançando, só que demorando mais do que o esperado. Você verá evidência como timestamps atualizados, contagens que crescem ou novas linhas de log/atualizações de progresso.
What’s the difference between a failed job and a stuck job?
Um job que falhou parou e não vai terminar sozinho. Ele termina com um erro ou encontra um problema sério, e a única forma de concluir o trabalho é corrigir a causa e reexecutar com segurança.
What is a heartbeat and why do I need it?
Um heartbeat é uma pequena atualização de “prova de vida” que o job grava enquanto roda, como um timestamp ou uma contagem processada. Se os heartbeats pararem de atualizar, você tem um sinal claro de que o job não está progredindo.
What’s the simplest way to add heartbeats without overengineering?
Registre um único registro por execução do job, por exemplo last_heartbeat_at, current_step e um número básico de progresso. Atualize somente depois de concluir uma unidade real de trabalho, assim o sinal reflete progresso real, não um loop que só gira.
How do I choose timeouts that won’t spam me with false alerts?
Comece com um soft timeout para sinalizar “mais lento que o normal” e um hard timeout para declarar “travado”. Um padrão prático: soft em 1.5–2× sua execução mais lenta normal e hard em 3–5×.
What are the most common real reasons jobs get stuck?
Causas comuns incluem: o worker caiu no meio da execução, um serviço de terceiros que fica pendente sem responder, bloqueio no banco de dados que impede atualizações, ou um bug que gera um loop repetindo o mesmo passo. Heartbeats mais um campo de “último passo” normalmente mostram qual é o caso.
What should a good stuck-job alert include?
Inclua nome do job, usuário/conta afetada, hora de início, último heartbeat, último passo conhecido e a primeira ação segura a ser tomada (reexecutar uma vez, pausar ou escalar). Se o alerta não disser o que fazer, ele será ignorado.
What’s a one-day plan to make stuck jobs diagnosable?
Comece pelo job que causa mais dor e defina em termos simples o que “concluído” significa. Adicione um heartbeat, defina uma regra de timeout e exiba quatro estados — Running, Done, Failed, Stuck — para que qualquer pessoa veja rapidamente o que está acontecendo.