Imagine a seguinte situação comum no dia a dia da engenharia de software: você precisa de um cron job que, a cada 5 minutos, verifica no banco de dados se existem registros que atendem uma condição e, se existirem, executa algumas ações neles.

À primeira vista, a solução parece trivial: basta agendar uma função que executa um SELECT * WHERE status = 'PENDING', itera sobre os resultados e dispara o processamento. No entanto, quando esse cenário vai para produção — com múltiplas instâncias da aplicação, volume crescente de dados e integrações instáveis —, a abordagem ingênua rapidamente resulta em race conditions, estouro de memória, travamento do banco e duplicação de efeitos colaterais.

A escolha da abordagem ideal depende diretamente da origem da condição e da volatilidade do volume de dados. A seguir, analisamos os riscos do modelo tradicional e como implementar soluções de alta resiliência.

5 min

intervalo típico de varredura periódica

100%

de risco de race condition sem lock distribuído

0 ms

overhead no banco com modelo Event-Driven

Resumo em 30 segundos

  • O risco da abordagem ingênua: Executar consultas abertas periodicamente sem controle de concorrência provoca sobreposição de rotinas, consumo excessivo de memória e processamento duplicado.
  • Abordagem A (Polling Robusto): Indicada para condições puramente temporais. Exige Lock Distribuído para a tarefa, processamento em lotes com FOR UPDATE SKIP LOCKED e transição rigorosa de estados.
  • Abordagem B (Event-Driven / Delayed Messaging): A alternativa superior para reações a ações do usuário. Substitui a varredura no banco por filas de mensagens diferidas (Delayed Queues) e o padrão Transactional Outbox.
  • Princípio da Idempotência: A ação executada no worker deve ser capaz de lidar com re-tentativas sem gerar efeitos colaterais duplicados (ex: cobranças duplas ou e-mails repetidos).

01 · Contexto

O que costuma dar errado na abordagem ingênua?

No início de um projeto, buscar todos os registros elegíveis a cada 5 minutos funciona perfeitamente. No entanto, conforme o sistema escala, quatro gargalos graves passam a afetar a infraestrutura:

Falha 1

Sobreposição (Overlapping)

Se o lote de registros crescer e o processamento demorar mais do que 5 minutos, o próximo ciclo do cron iniciará antes do término do anterior, criando um efeito de bola de neve que consome os recursos da aplicação.

Falha 2

Múltiplas Instâncias

Se a aplicação rodar em um cluster com 5 pods/containers (Kubernetes), todas as 5 instâncias dispararão o cron no mesmo instante, selecionando os mesmos registros e disparando ações duplicadas.

Falha 3

Gargalo de Memória e Banco

Consultas do tipo SELECT * sem paginação em tabelas com centenas de milhares de linhas geram picos abruptos de I/O de disco, locking de tabela e picos de memória (OOM) no worker.

Falha 4

Acoplamento Temporal

Registros que entram na condição precisam esperar até 5 minutos para serem processados, criando um atraso desnecessário se a regra de negócio exigir resposta rápida.

02 · Polling Robusto

Abordagem A: Polling Robusto em Banco de Dados

Se a condição for puramente baseada em tempo (ex: "cancelar pedidos pendentes há mais de 24 horas") e o cron job for a única opção viável, a consulta deve ser estruturada para garantir concorrência segura e alto desempenho.

1. Lock Distribuído

Para impedir que múltiplas instâncias executem o agendador simultaneamente, deve-se implementar uma trava centralizada via chave temporária. Ferramentas como Redis (Redlock), ShedLock ou PostgreSQL Advisory Locks garantem que apenas um nó execute a tarefa naquele intervalo de 5 minutos.

2. Batching com FOR UPDATE SKIP LOCKED

Em vez de ler a tabela inteira, o worker processa dados em pequenos lotes (ex: 100 em 100 registros), bloqueando individualmente as linhas que está operando sem travar as consultas paralelas do restante do sistema:

Consulta SQL para Concorrência Segura

BEGIN;

SELECT id, status 
FROM processamento_pedidos 
WHERE status = 'PENDING' 
  AND created_at <= NOW() - INTERVAL '5 minutes'
ORDER BY created_at ASC 
LIMIT 100 
FOR UPDATE SKIP LOCKED;

-- Transição imediata de estado dentro da mesma transação
UPDATE processamento_pedidos 
SET status = 'IN_PROGRESS', updated_at = NOW() 
WHERE id IN (...);

COMMIT;

O modificador SKIP LOCKED instrui o banco de dados a ignorar automaticamente os registros que já estão sendo processados por outras threads ou instâncias secundárias. Isso permite escalar horizontalmente o número de workers sem gerar conflito ou sobreposição de dados.

03 · Event-Driven

Abordagem B: Arquitetura Orientada a Eventos e Mensageria

Na maioria dos sistemas modernos, o polling a cada 5 minutos pode — e deve — ser substituído por um modelo Event-Driven. Se a condição no banco de dados muda em resposta a uma ação (ex: finalização de cadastro, alteração de status ou emissão de nota), a aplicação não deve consultar o banco continuamente para descobrir a mudança.

Padrão 1

Transactional Outbox Pattern

No momento em que o registro principal é gravado, insere-se um evento em uma tabela de outbox dentro da mesma transação. Um relay leve publica essa mensagem em um broker (RabbitMQ, SQS ou Kafka), eliminando locks no banco principal.

Padrão 2

Delayed Delivery / Message Queues

Se a regra exige aguardar exatamente 5 minutos após um evento para executar uma ação, o evento é publicado diretamente em uma fila de mensagens diferidas (Delay Queue do AWS SQS ou plugin de atraso do RabbitMQ).

Eliminar o polling reduz o estresse de leitura no banco de dados a zero, mantendo a I/O dedicada às operações transacionais críticas.

04 · Comparativo

Qual abordagem escolher? Matriz de Decisão

A tabela a seguir resume quando utilizar cada estratégia com base nos requisitos arquiteturais da sua aplicação:

Critérios de Escolha Arquitetural

Origem da Condição

Tempo puro (ex: expiração de token após 24h): Polling Robusto com SKIP LOCKED.
Mudança de Estado/Ação do Usuário: Mensageria / Event-Driven.

Volatilidade de Volume

Picos imprevisíveis: Filas com consumo assíncrono (gerencia Backpressure naturalmente).
Volume estável e baixo: Cron Job otimizado com paginação e limite fixo.

Latência Tolerada

Próxima de zero: Arquitetura orientada a eventos.
Até 5 minutos de atraso aceitáveis: Cron Job agendado.

05 · Idempotência

A regra de ouro: Idempotência e Tratamento de Erros

Independentemente da abordagem escolhida (Polling ou Filas), o worker que executa a ação deve ser estritamente idempotente. Falhas de rede, timeouts de APIs externas e reinicializações de contêineres podem fazer com que a mesma tarefa seja entregue duas vezes.

  • Chave de Idempotência: Utilize uma chave única (ex: uuid_tarefa + tentativa) para evitar disparos repetidos de e-mails ou cobranças em gateways.
  • Circuit Breaker & Retry Limit: Se uma integração externa falhar durante a execução da ação, defina um limite claro de retentativas com exponential backoff.
  • Dead Letter Queue (DLQ) / Status FAILED: Registros que falharem consecutivamente devem ser marcados como FAILED ou enviados para uma DLQ, permitindo auditoria sem travar os próximos ciclos do cron.

06 · FAQ

Perguntas Frequentes sobre Cron Jobs e Banco de Dados

O comando FOR UPDATE SKIP LOCKED funciona em qual banco de dados?

O recurso é suportado nativamente pelos principais bancos relacionais modernos, como PostgreSQL (a partir da versão 9.5), MySQL (a partir da versão 8.0), Oracle Database e SQL Server (via READPAST).

Por que não usar apenas um flag Booleano como processado = true?

Atualizar uma flag booleana após o processamento abre margem para race conditions entre a leitura do registro e o momento da escrita. Se a ação demorar alguns segundos, outro worker lerá o registro com processado = false e executará a ação novamente. A transição deve ocorrer no nível de linha no momento da seleção.

Como garantir que apenas uma instância do Kubernetes rode o cron?

A solução recomendada é utilizar locks distribuídos (como Redis ou ShedLock). Alternativamente, em arquiteturas Kubernetes, pode-se separar a rotina agendada em um recurso do tipo CronJob isolado, desacoplado dos Pods da API principal.

Próximo passo · Diagnosticando sua Arquitetura

Sua aplicação sofre com instabilidade no banco de dados, locks de consulta ou picos de latência causados por processamentos em segundo plano? Converse com os especialistas em Engenharia de Performance da FATTO Consultoria e Sistemas e otimize sua infraestrutura.

Agendar avaliação de arquitetura