Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Delayed Jobs no Node.js: agende tarefas com segurança

Atualizado em: 26 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

Delayed Jobs no Node.js representam tarefas que só devem ficar disponíveis depois de um instante específico. Lembretes, expiração de reservas, reprocessamentos, follow-ups e notificações programadas são exemplos comuns.

Um delay não garante execução no horário exato. Ele define o primeiro momento em que o job pode ser consumido. Atraso real depende da saúde do scheduler, capacidade dos workers, relógios, Redis ou banco e limites de concorrência.

Delay relativo

await queue.add('send-reminder', {
  userId,
  reminderId
}, {
  delay: 30 * 60 * 1000,
  jobId: `reminder:${reminderId}`
});

O job fica elegível aproximadamente trinta minutos depois. Não use delay relativo para regras de calendário sem calcular o instante de negócio corretamente.

Horário absoluto

const runAt = new Date('2026-10-30T09:00:00-03:00');
const delay = Math.max(0, runAt.getTime() - Date.now());

await queue.add('send-reminder', data, { delay });

Persista runAt em UTC e preserve o fuso original quando ele faz parte da regra.

Fonte de verdade

Não dependa apenas do estado interno da fila. Mantenha uma tabela de agendamentos.

CREATE TABLE scheduled_jobs (
  id uuid PRIMARY KEY,
  type text NOT NULL,
  payload jsonb NOT NULL,
  run_at timestamptz NOT NULL,
  status text NOT NULL,
  queue_job_id text,
  version integer NOT NULL DEFAULT 1,
  created_at timestamptz NOT NULL DEFAULT now()
);

A tabela permite auditoria, cancelamento, rescheduling e recuperação caso o job desapareça da fila.

Padrão scheduler + queue

  1. a aplicação persiste o agendamento;
  2. um scheduler busca itens próximos;
  3. adiciona jobs com ID determinístico;
  4. marca o ID da fila;
  5. o worker revalida estado e horário;
  6. processa idempotentemente.

Busca de agendamentos

SELECT id, type, payload, run_at, version
FROM scheduled_jobs
WHERE status = 'scheduled'
  AND run_at <= now() + interval '15 minutes'
ORDER BY run_at
FOR UPDATE SKIP LOCKED
LIMIT 100;

Uma janela pequena reduz quantidade de jobs distantes mantidos na fila e facilita alterações.

Revalidando no worker

const schedule = await repository.findById(job.data.scheduleId);

if (!schedule || schedule.status !== 'scheduled') {
  return { skipped: true };
}

if (schedule.version !== job.data.version) {
  return { skipped: true, reason: 'superseded' };
}

if (schedule.runAt.getTime() > Date.now()) {
  throw new RetryLaterError(schedule.runAt);
}

O worker não deve confiar cegamente no payload antigo da fila.

Rescheduling

Ao alterar o horário:

  1. atualize run_at;
  2. incremente a versão;
  3. marque o job anterior como obsoleto ou remova se possível;
  4. agende o novo job;
  5. o worker antigo detecta versão diferente e ignora.

Cancelamento

UPDATE scheduled_jobs
SET status = 'cancelled', version = version + 1
WHERE id = $1 AND status = 'scheduled';

Mesmo que o job já esteja ativo, ele deve verificar cancelamento antes do efeito externo.

Idempotência

Um delayed job pode ser executado mais de uma vez após falha ou timeout. Use uma chave de intenção.

const effectKey = `reminder:${schedule.id}:${schedule.version}`;

Persista o efeito ou use constraint única no destino.

Precisão

Para lembretes, atraso de segundos costuma ser aceitável. Para leilões, reservas e pagamentos, a regra de negócio não deve depender apenas de um job que “fecha” no horário. O endpoint que recebe ações também precisa validar o prazo no banco.

Clock skew

Use relógios sincronizados e prefira a hora do banco para decisões críticas.

SELECT now();

Calcular tudo em processos com relógios divergentes pode liberar jobs cedo ou tarde.

Retries atrasados

Retries e jobs agendados são conceitos diferentes. Um retry representa nova tentativa da mesma execução; um delayed job representa intenção futura. Registre ambos separadamente.

Backoff

Para erro transitório, calcule próxima tentativa com jitter. Não substitua o horário original sem guardar histórico.

Lotes de jobs no mesmo minuto

Campanhas e fechamentos podem criar milhares de jobs para o mesmo instante. Aplique dispersão controlada quando a regra permitir.

const jitter = Math.floor(Math.random() * 60_000);
const delay = baseDelay + jitter;

Se o horário precisa ser exato, dimensione workers e dependências para o pico.

Multi-tenancy

Inclua tenant na chave e aplique quotas. Um tenant não deve preencher a fila e atrasar os demais.

Payload mínimo

Armazene IDs e versão, não uma cópia completa dos dados. Quando o job rodar, busque o estado atual e valide autorização e status.

Observabilidade

Monitore:

  • diferença entre run_at e início real;
  • jobs atrasados;
  • agendamentos sem job;
  • jobs sem agendamento;
  • cancelamentos;
  • reschedules;
  • tentativas;
  • idade do item mais atrasado.

Reconciliação

Execute periodicamente um job que encontra agendamentos vencidos ainda em estado scheduled e os reenvia para a fila. A chave determinística impede duplicação descontrolada.

Graceful shutdown

O scheduler deve parar de reservar novos itens e concluir as inserções em andamento. O worker termina ou libera jobs ativos de forma recuperável.

Testes

  • delay relativo;
  • horário absoluto e fuso;
  • job atrasado;
  • cancelamento antes e durante execução;
  • rescheduling;
  • versão obsoleta;
  • clock skew;
  • reconciliação;
  • duplicação;
  • pico de jobs.

Erros comuns

  • usar fila como única fonte de verdade;
  • achar que delay é execução exata;
  • não revalidar estado;
  • não versionar reschedule;
  • cancelar apenas na fila;
  • payload completo e obsoleto;
  • sem reconciliação;
  • ignorar pico no mesmo horário;
  • não medir atraso real.

Checklist

  • run_at persistido;
  • UTC e fuso definidos;
  • ID determinístico;
  • versão do agendamento;
  • cancelamento persistido;
  • worker revalida;
  • idempotência;
  • reconciliação;
  • métricas de atraso;
  • capacidade para picos.

Conclusão

Delayed Jobs no Node.js precisam de estado persistido e revalidação. A fila é um mecanismo de entrega, não a autoridade final sobre horário, cancelamento e versão.

Registre o agendamento, use IDs estáveis, reconcilie itens perdidos e meça atraso real. Com essa arquitetura, lembretes e expirações continuam corretos mesmo durante reinícios e falhas.

Consulte a documentação de delayed jobs do BullMQ e a documentação de Timers do Node.js.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita