Pipelines de Processamento com Filas e BullMQ

December 12, 2025 (10mo ago) · Atualizado em July 29, 2026 (2mo ago) · Thiago Vieira

Um pipeline de processamento orientado a filas tira o trabalho pesado do caminho da requisição para que picos de tráfego não derrubem sua API. Com Redis e BullMQ, cada etapa vira uma unidade de trabalho controlada, com retentativas, limites de concorrência, timeouts e observabilidade. O objetivo é ter um tratamento de falhas previsível quando a ingestão dispara, uma API externa fica lenta ou um worker cai no meio de um job.

O que é um pipeline de processamento orientado a filas?

Um pipeline orientado a filas é uma arquitetura em que a API aceita o trabalho rapidamente, persiste um identificador de negócio imutável e agenda jobs assíncronos para as etapas seguintes. Etapas típicas:

ingestão -> validação -> enriquecimento -> entrega

Cada etapa grava seu status no armazenamento e enfileira o próximo job com um payload que referencia IDs, em vez de embutir blobs grandes. Os workers buscam o estado atual pelo ID, fazem o trabalho, atualizam o status e então seguem adiante no pipeline ou param com um erro recuperável.

Por que usar BullMQ e Redis nisso?

O BullMQ oferece filas nomeadas, jobs com atraso, retentativas, limites de taxa e eventos de job sobre o Redis. Essa combinação serve bem para fluxos de produto como processamento de mídia, distribuição de webhooks, geração de relatórios, efeitos colaterais de pagamentos e sincronização com CRM. O Redis é rápido o bastante para coordenar jobs, enquanto o banco de dados principal continua sendo a fonte da verdade do estado de negócio.

Use filas quando:

  • O trabalho pode demorar mais do que um timeout HTTP razoável.
  • Picos sobrecarregariam uma API de terceiros se fossem tratados na hora.
  • Falhas precisam de retentativas sem bloquear o usuário.
  • Várias etapas precisam de isolamento para que uma etapa lenta não trave as demais.

Padrões de confiabilidade que importam

  • Chave de idempotência por operação de negócio, para que retentativas não enviem nem gravem em duplicidade.
  • Backoff exponencial com retentativas limitadas e, depois, um caminho de dead-letter.
  • Dead-letter queues ou inspeção de jobs com falha para recuperação manual.
  • Métricas por etapa: jobs na fila, jobs ativos, atraso, taxa de sucesso e duração p95.
  • Timeouts explícitos, para que jobs travados apareçam como falhas e não como backlog invisível.
  • Tratamento de poison messages: payloads que nunca vão funcionar sem uma correção de código.

Idempotência é a diferença entre "tentar de novo com segurança" e "tentar de novo e criar o caos". Derive a chave da operação de negócio, não de um UUID aleatório gerado dentro do worker.

Dicas práticas para workers estáveis

  • Mantenha os payloads pequenos e busque o estado por ID nos workers.
  • Imponha timeouts nos jobs; jobs travados escondem o backlog real.
  • Acompanhe separadamente o atraso da fila e o tempo de processamento.
  • Limite a concorrência por fila com base nos limites do que vem a jusante, não só na CPU.
  • Separe filas críticas (pagamentos, atualizações de conta) de filas de volume (exportações, backfills).
  • Deixe as transições de status explícitas: received, processing, succeeded, failed, dead_letter.
  • Prefira entrega at-least-once com handlers idempotentes a fingir que as mensagens são exactly-once.

Um modelo de status concreto

Guarde uma linha para o objeto de negócio com pipeline_stage, last_error, attempt_count e timestamps. O payload do job deve incluir o ID do objeto e a etapa esperada. Se um worker pegar um job de uma etapa que já foi concluída, ele termina com sucesso. Se a etapa estiver errada, falhe de forma visível para que os operadores enxerguem um erro de sequenciamento.

Quando não usar uma fila

Não coloque toda escrita no banco atrás de uma fila. Leituras voltadas ao usuário que precisam ser consistentes de imediato, e validações pequenas que terminam em milissegundos, geralmente pertencem à própria requisição. Filas brilham quando o trabalho é lento, irregular, depende de sistemas externos ou precisa de retentativas controladas.

Como observar um pipeline em produção?

Acompanhe quatro sinais por etapa: taxa de enfileiramento, taxa de processamento, atraso (a idade do job mais antigo na espera) e taxa de falhas. O atraso mostra que o sistema está ficando para trás mesmo quando a taxa de erros parece boa. A taxa de falhas mostra que um problema de dependência ou de payload está queimando retentativas. Combine isso com SLOs de negócio, como "mídia pronta em até 5 minutos" ou "efeitos de webhook em até 30 segundos".

Exponha também um caminho para o operador: inspecionar um job, ver suas tentativas, ler o último erro e reenfileirar com segurança. Sem isso, engenheiros entram no Redis por SSH e ficam adivinhando.

Como é uma boa primeira implementação?

Comece com um caminho crítico, uma fila por etapa, handlers idempotentes e uma tabela de status. Adicione o tratamento de dead-letter antes de qualquer roteamento sofisticado. Só então separe o trabalho de alta prioridade do trabalho em volume. A maioria das equipes exagera na topologia e subinveste em observabilidade e idempotência.

Essa arquitetura ajuda também em tráfego médio, porque os modos de falha continuam previsíveis. Se você precisa de um sistema de ingestão ou processamento baseado em filas, construído ou estabilizado, esse é um padrão comum de contratação no meu trabalho de backend.