Anti-padrões, 08/10/2026, 6 min de leitura

O perigo do @Scheduled do Spring com mais de um pod

Em produção, cada instância roda o seu próprio agendador. Com dois pods, o job executa duas vezes.

Em desenvolvimento, o job roda uma vez por minuto e tudo parece certo. Em produção, você sobe dois pods e o mesmo job passa a rodar duas vezes por minuto: cobrança em duplicidade, e-mail repetido, arquivo processado duas vezes.

Por que acontece

O @Scheduled é local à JVM. Ele não sabe que existe outra instância da aplicação. Cada pod tem o seu agendador e cada um dispara o método no horário.

@Scheduled(cron = "0 */5 * * * *")
public void processarPendentes() {
    // roda em TODOS os pods, ao mesmo tempo
    pendenteService.processar();
}

Escalar horizontalmente, algo que deveria ser seguro, muda o comportamento do sistema. Esse é o sinal do anti-padrão.

Opções de correção

1. Lock distribuído com ShedLock. Mantém o @Scheduled e garante que só uma instância execute por vez, usando uma tabela no banco (ou Redis) como trava.

@Scheduled(cron = "0 */5 * * * *")
@SchedulerLock(name = "processarPendentes",
               lockAtMostFor = "4m",
               lockAtLeastFor = "30s")
public void processarPendentes() {
    pendenteService.processar();
}

2. Quartz em modo cluster. Mais pesado, mas oferece persistência dos jobs e balanceamento entre nós.

3. Tirar o agendamento da aplicação. Um CronJob do Kubernetes dispara o processamento, que roda uma vez por execução. Faz sentido quando o job não precisa viver dentro do serviço.

Os cuidados que ficam

  • lockAtMostFor menor que a duração real do job libera o lock no meio da execução e o segundo pod entra. Dimensione com folga.
  • Relógios diferentes entre os pods desalinham a disputa pelo lock.
  • O lock reduz a duplicidade, mas não garante zero. Se o pod cair no meio, o job pode rodar de novo. O processamento precisa ser idempotente.

Regra prática

Todo método agendado deve responder a duas perguntas: "o que acontece se rodar em dois pods?" e "o que acontece se rodar duas vezes?". Se a resposta for "dano", falta lock e falta idempotência.