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

BullMQ no Node.js: filas, retries e workers

Atualizado em: 26 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

BullMQ no Node.js implementa filas de jobs sobre Redis com workers, retries, delays, prioridades, eventos e agendamento. A biblioteca é útil para mover tarefas lentas para processamento assíncrono, como envio de e-mails, geração de arquivos, importações e integrações externas.

Uma fila não elimina falhas. Jobs podem ser entregues novamente, workers podem morrer no meio da execução e Redis pode ficar indisponível. O código precisa ser idempotente, limitado e observável.

Conexão Redis

import { Queue, Worker } from 'bullmq';

const connection = {
  host: process.env.REDIS_HOST,
  port: Number(process.env.REDIS_PORT),
  password: process.env.REDIS_PASSWORD,
  tls: process.env.REDIS_TLS === 'true' ? {} : undefined
};

const emailQueue = new Queue('email', { connection });

Use TLS e autenticação quando Redis estiver fora de uma rede confiável. Não compartilhe um banco Redis sem namespace e políticas de capacidade.

Adicionando um job

await emailQueue.add(
  'send-welcome',
  {
    userId,
    template: 'welcome-v2'
  },
  {
    jobId: `welcome:${userId}:v2`,
    attempts: 5,
    backoff: {
      type: 'exponential',
      delay: 5000
    },
    removeOnComplete: 1000,
    removeOnFail: 5000
  }
);

Evite colocar dados pessoais ou segredos no payload. Prefira IDs e busque o estado atualizado durante a execução.

Criando um worker

const worker = new Worker(
  'email',
  async job => {
    switch (job.name) {
      case 'send-welcome':
        return sendWelcome(job.data);
      default:
        throw new Error(`Job desconhecido: ${job.name}`);
    }
  },
  {
    connection,
    concurrency: 10
  }
);

Valide job.name, versão e payload. Jobs antigos podem permanecer na fila durante um deploy.

Idempotência

Um worker pode executar o mesmo job mais de uma vez. A aplicação precisa garantir que efeitos não sejam duplicados.

await db.query(
  `INSERT INTO sent_notifications (notification_key, sent_at)
   VALUES ($1, now())
   ON CONFLICT DO NOTHING`,
  [notificationKey]
);

A constraint deve envolver a intenção de negócio, não apenas o ID interno do BullMQ.

jobId

Um jobId estável ajuda a impedir duplicações enquanto o job correspondente ainda existe. Se jobs concluídos são removidos, o mesmo ID pode ser adicionado novamente no futuro. Por isso, mantenha idempotência também no banco ou serviço de destino.

Retries

Classifique erros:

  • transitórios: timeout, 429 e falhas de rede;
  • permanentes: payload inválido, recurso inexistente e credencial revogada;
  • desconhecidos: exigem observabilidade e limite.

Não faça retry de erro permanente. Veja Retry com Backoff no Node.js.

Timeout e cancelamento

Defina deadline dentro do processor.

async function sendWelcome(data) {
  const signal = AbortSignal.timeout(15_000);
  return mailer.send({ ...data, signal });
}

Uma Promise rejeitada não cancela automaticamente a operação subjacente. Propague AbortSignal quando a biblioteca aceitar.

Concorrência

Concorrência alta aumenta throughput de I/O, mas pode esgotar banco, APIs e CPU. Defina limites por recurso.

  • pool do banco;
  • conexões HTTP;
  • rate limit do provedor;
  • memória por job;
  • CPU disponível.

Use filas separadas ou workers com concorrências diferentes para workloads distintos.

Prioridades

await queue.add('generate-report', data, { priority: 10 });

Prioridade pode causar starvation de jobs comuns. Monitore idade dos itens e não crie dezenas de níveis sem necessidade.

Jobs atrasados

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

Delay não é garantia de execução no milissegundo exato. Ele indica quando o job se torna elegível, dependendo de workers e capacidade.

Repeatable e schedulers

Tarefas recorrentes devem ter chave e timezone documentados. Mesmo com scheduler da biblioteca, a execução precisa ser idempotente.

Consulte Cron Jobs no Node.js.

Flows

Fluxos podem representar dependências entre jobs. Use com moderação: workflows complexos exigem compensação, estados claros e ferramentas operacionais.

Progresso

await job.updateProgress({ processed: 500, total: 1000 });

Atualizações frequentes geram carga em Redis. Atualize por lote ou percentual significativo.

Eventos

Eventos de conclusão e falha ajudam na observabilidade, mas não devem ser a única fonte de estado de negócio. Persista resultados importantes.

Falhas e retenção

Mantenha jobs falhos por tempo suficiente para diagnóstico, com limite para não consumir memória indefinidamente. Para erros permanentes, use fila ou estado dead letter.

Veja Dead Letter Queue no Node.js.

Redis e durabilidade

Configure persistência e replicação conforme o impacto de perder jobs. Redis usado apenas como cache pode ter políticas inadequadas para fila. Evite eviction das chaves do BullMQ.

Consulte Redis com Node.js.

Payload versionado

{
  "schemaVersion": 2,
  "userId": "u-1",
  "template": "welcome-v2"
}

Durante deploy, workers antigos e novos podem coexistir. Aceite versões suportadas ou encaminhe job incompatível para análise.

Multi-tenancy

Inclua tenant no payload e nas chaves de idempotência, mas valide o tenant a partir de contexto confiável. Aplique limites por tenant para impedir noisy neighbor.

Observabilidade

Monitore:

  • jobs waiting, active, completed e failed;
  • idade do job mais antigo;
  • duração;
  • tentativas;
  • throughput;
  • event loop lag;
  • conexões Redis;
  • falhas por tipo.

Graceful shutdown

async function shutdown() {
  await worker.close();
  await emailQueue.close();
}

process.once('SIGTERM', shutdown);
process.once('SIGINT', shutdown);

worker.close() para de buscar novos jobs e aguarda os ativos conforme o comportamento da biblioteca. Configure também timeout externo do processo.

Testes

  • job válido;
  • payload inválido;
  • retry transitório;
  • erro permanente;
  • job duplicado;
  • worker encerrado no meio;
  • Redis indisponível;
  • concorrência;
  • delay;
  • versão antiga do payload.

Erros comuns

  • payload com dados sensíveis;
  • achar que jobId garante idempotência permanente;
  • retry de qualquer erro;
  • concorrência maior que dependências;
  • eviction no Redis;
  • remover falhas cedo demais;
  • não versionar payload;
  • não fechar worker no deploy;
  • usar eventos como estado durável.

Checklist

  • Redis protegido e persistente;
  • payload pequeno;
  • schema versionado;
  • idempotência de negócio;
  • retry classificado;
  • timeout;
  • concorrência medida;
  • retenção limitada;
  • métricas e alertas;
  • shutdown gracioso.

Conclusão

BullMQ no Node.js oferece uma base completa para filas sobre Redis. Ele resolve agendamento e coordenação operacional, mas a confiabilidade final depende do processor.

Use payloads pequenos, idempotência persistida, retries seletivos e limites de concorrência. Com Redis configurado para durabilidade e workers encerrados corretamente, BullMQ suporta workloads assíncronos previsíveis.

Consulte a documentação oficial do BullMQ e a documentação do Redis.

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