Voltar ao blog
Background jobsCronWorkersProcessamento assíncronoBackend

Background jobs: cron, filas de trabalho e processamento assíncrono confiável

2026-11-14
Background jobs: cron, filas de trabalho e processamento assíncrono confiável

Nem tudo em um sistema pode acontecer dentro do tempo de uma requisição HTTP. O usuário clica em "exportar relatório" e não pode ficar dez minutos com a tela travada esperando. Um pagamento aprovado precisa disparar um e-mail, atualizar um sistema externo e gerar uma nota, e nada disso deveria segurar a resposta ao usuário. À meia-noite, alguém tem que calcular as cobranças do dia. Todo esse trabalho que roda fora do ciclo de requisição é o domínio dos background jobs, e fazê-los de forma confiável é mais sutil do que parece.

Este guia cobre o processamento assíncrono interno de um sistema: como agendar tarefas, distribuir trabalho entre workers, lidar com falhas sem perder nem duplicar execução, e processar jobs longos. O foco é o trabalho de fundo dentro da sua aplicação, um tema distinto da mensageria que transporta eventos entre serviços.

Por que tirar trabalho do ciclo de requisição

Uma requisição HTTP deve ser rápida. Tudo que é lento, pesado ou que pode ser feito depois deve sair do caminho síncrono e virar um job de fundo. Os motivos:

  • Responder rápido ao usuário. A tela devolve "estamos processando" na hora, e o trabalho pesado corre por trás.
  • Absorver picos. Uma enxurrada de tarefas entra em uma fila e é processada no ritmo que os workers aguentam, sem derrubar a aplicação.
  • Isolar falhas. Se o envio de um e-mail falha, isso não pode quebrar a resposta da requisição que o originou.
  • Escalar o processamento sozinho. Você adiciona workers para dar conta de mais trabalho, sem tocar na camada que atende requisições.

Há dois grandes tipos de trabalho de fundo: o agendado, que roda em horários definidos, e o sob demanda, disparado por um evento e enfileirado para um worker processar.

Trabalho agendado: o cron

O cron é o mecanismo clássico de agendamento: executar uma tarefa em horários ou intervalos definidos. Relatórios noturnos, limpezas periódicas, cobranças mensais, sincronizações a cada hora. Simples em uma máquina só, o cron ganha uma armadilha assim que a aplicação roda em várias instâncias.

Se você tem cinco servidores e cada um dispara o mesmo cron à meia-noite, a tarefa roda cinco vezes. Cobrar o cliente cinco vezes, enviar o relatório cinco vezes. O agendamento distribuído precisa garantir que a tarefa execute uma vez só, e é aí que entram os locks distribuídos, adiante. A regra é clara: em ambiente com múltiplas instâncias, nunca confie em um cron ingênuo por processo sem um mecanismo de exclusão.

Trabalho sob demanda: filas e workers

Para trabalho disparado por eventos, o padrão é a fila de trabalho com workers. A aplicação, ao receber a requisição, apenas enfileira um job (com os dados necessários) e responde ao usuário na hora. Do outro lado, um conjunto de workers consome a fila e processa cada job no seu ritmo.

Essa separação entre quem produz o trabalho e quem o executa traz elasticidade: em um pico, a fila cresce e segura o trabalho; você sobe mais workers para drenar mais rápido; na calmaria, reduz. O tamanho da fila (o backlog) vira um termômetro direto da saúde do processamento.

O que torna um job confiável

Enfileirar é fácil. O difícil é garantir que o trabalho realmente aconteça, exatamente uma vez em efeito, mesmo diante de falhas. Alguns pilares.

Retries com backoff exponencial

Jobs falham por motivos transitórios: um serviço externo fora do ar, um timeout momentâneo. A resposta é repetir, mas com cuidado. O backoff exponencial aumenta o intervalo entre tentativas (1 min, 5 min, 15 min), dando tempo para o problema passar em vez de martelar um serviço já em dificuldade. Some a isso um jitter (variação aleatória) para evitar que muitos jobs repitam no mesmo instante e criem uma nova onda de carga.

Idempotência

Como um job pode ser reexecutado (por um retry ou porque o worker caiu depois de processar mas antes de confirmar), ele precisa ser idempotente: rodar duas vezes tem que ter o mesmo efeito de rodar uma. Um job de "enviar e-mail de boas-vindas" deve verificar se já enviou antes de enviar de novo. Sem idempotência, cada retry vira um efeito duplicado.

Dead letter queue

Um job que falha repetidamente não pode ser tentado para sempre nem travar a fila. Depois de um número máximo de tentativas, ele vai para a dead letter queue (DLQ), uma fila separada de jobs que precisam de atenção humana. Lá eles são inspecionados, corrigidos e reprocessados, sem bloquear o fluxo saudável. Toda operação séria de jobs monitora a DLQ, porque ela é onde os problemas reais se acumulam.

Locks distribuídos

Para impedir que a mesma tarefa rode em paralelo em vários workers (o problema do cron em múltiplas instâncias), usa-se um lock distribuído: antes de executar, o worker tenta adquirir um lock compartilhado (por exemplo, em um Redis). Só quem consegue o lock executa; os outros desistem. O lock precisa ter expiração, para que a morte do worker que o segura não trave a tarefa para sempre.

Visibilidade e timeout

Quando um worker pega um job, o job fica invisível para os outros workers por um tempo (o visibility timeout). Se o worker terminar, confirma e o job some. Se o worker morrer sem confirmar, o job reaparece na fila após o timeout e outro worker o pega. Esse mecanismo é o que garante que um job não se perde quando um worker cai no meio do processamento.

Panorama dos mecanismos

Mecanismo Problema que resolve
Fila + workers Tirar trabalho pesado do ciclo de requisição
Retry com backoff e jitter Falhas transitórias sem afogar dependências
Idempotência Reexecução sem efeito duplicado
Dead letter queue Jobs que falham sempre, sem travar a fila
Lock distribuído Não executar a mesma tarefa em paralelo
Visibility timeout Não perder job quando o worker morre

Jobs longos: quebrar em pedaços

Um job que roda por horas é frágil: se ele falha aos 90%, um retry recomeça do zero, e ele monopoliza um worker por muito tempo. A prática madura é quebrar o trabalho grande em muitos jobs pequenos. Processar um milhão de registros vira mil jobs de mil registros cada, idealmente com checkpoints que registram o progresso. Se um pedaço falha, só ele é repetido, e o trabalho retoma de onde parou, não do começo. Jobs pequenos são mais resilientes, paralelizáveis e observáveis que um monólito de processamento.

Observabilidade dos jobs

Trabalho de fundo é, por natureza, invisível, e o que é invisível costuma ser negligenciado até falhar. Por isso, jobs precisam de observabilidade de primeira classe: o tamanho do backlog, a taxa de falhas, o tempo de processamento, o volume na DLQ. Um backlog que só cresce é o alarme mais precoce de que os workers não dão conta, e você quer descobrir isso por um gráfico, não porque um cliente reclamou que o relatório nunca chegou.

Trabalho invisível, confiabilidade visível

Background jobs são a espinha dorsal silenciosa de todo SaaS: e-mails, relatórios, cobranças, sincronizações, tudo que acontece longe dos olhos do usuário. Fazê-los bem não é enfileirar e torcer, e sim projetar para a falha desde o início, com retries sensatos, idempotência, DLQ, locks e observabilidade. É esse cuidado que separa um sistema onde o trabalho de fundo "simplesmente funciona" de um onde tarefas somem, duplicam ou travam sem ninguém perceber até ser tarde.


Suas tarefas de fundo somem, duplicam ou travam sem aviso? Na ConstructorTI, projetamos processamento assíncrono confiável, com agendamento seguro, workers escaláveis, retries com backoff, DLQ e locks distribuídos. Se relatórios não chegam, cobranças duplicam ou a fila de jobs vive entupida, fale com a nossa engenharia e receba um diagnóstico do seu processamento em segundo plano.