Pular para o conteúdo principal

Conceitointermediário

Filas

Visão Geral

Uma fila recebe mensagens de um produtor e as entrega a um consumidor, que as processa no seu próprio ritmo.

O que ela compra é desacoplamento temporal: o produtor não depende da disponibilidade do consumidor. O que ela cobra são três garantias que passam a ser responsabilidade da aplicação — e que quase sempre são descobertas em produção.

Problema

Chamada síncrona amarra produtor e consumidor no tempo. Se o consumidor está fora, o produtor está fora. Se o consumidor é lento, o produtor fica lento. Um pico no produtor vira pico no consumidor.

A fila corta isso: o produtor publica e segue; a fila absorve; o consumidor processa quando consegue.

O erro comum não é adotar fila — é adotá-la sem reconhecer que o canal é uma rede, e que rede traz duplicação, desordem e mensagens que nunca processam.

Conceitos Centrais

O que a fila absorve

Indisponibilidade do consumidor. Mensagens acumulam; nada se perde.

Diferença de ritmo. O produtor pode publicar mais rápido do que o consumidor processa, por um tempo.

Pico. A fila cresce em vez de o sistema cair. É o mecanismo de contenção mais barato que existe.

Nenhum dos três é infinito — a fila tem limite, e o que acontece ao atingi-lo precisa ser decidido. Ver backpressure.

As três garantias que você herda

Duplicação. Praticamente todo sistema de fila entrega ao menos uma vez. Isso significa que a mesma mensagem pode chegar duas vezes — por retentativa, por falha na confirmação, por rebalanceamento de consumidores.

A consequência é dura e inegociável: o consumidor precisa ser idempotente. Processar duas vezes tem que ter o mesmo efeito de processar uma. Sem isso, uma cobrança vira duas.

Ordem. Filas particionadas garantem ordem dentro de uma partição, não entre elas. Se PedidoCriado e PedidoCancelado caem em partições diferentes, podem chegar fora de ordem.

A mitigação usual é particionar pela chave da entidade — todos os eventos de um pedido na mesma partição — o que preserva a ordem que importa ao custo de desequilíbrio se uma chave for muito ativa.

Mensagem envenenada. Uma mensagem que sempre falha volta para a fila, indefinidamente, bloqueando o processamento. É o que exige dead-letter queue: após N tentativas, a mensagem sai para uma fila separada, com alerta.

Uma fila sem dead-letter configurada trava no primeiro dado malformado.

Confirmação depois de processar

O consumidor confirma a mensagem após processá-la com sucesso, nunca antes. Se confirmar antes e falhar, a mensagem se perde.

O tempo de visibilidade — quanto a fila espera antes de reentregar — precisa ser maior que o tempo de processamento. Se for menor, a mensagem é reentregue enquanto ainda está sendo processada, e o resultado é duplicação garantida.

A fila esconde o problema até não esconder

Uma fila crescendo é sintoma, não solução. Se o consumidor é permanentemente mais lento que o produtor, a fila só adia o colapso.

Monitorar profundidade e idade da mensagem mais antiga é o que transforma isso em alerta antes de virar incidente.

Modelo Mental

A fila troca "falha agora" por "atraso agora, e talvez falha depois". Isso é bom quando o atraso é aceitável e ruim quando ele apenas esconde uma incapacidade.

Quando Usar

  • O produtor não precisa do resultado para continuar.
  • O consumidor pode processar com atraso.
  • É preciso absorver pico.
  • O consumidor é menos disponível que o produtor.
  • O trabalho é demorado e não cabe numa requisição.

Quando Não Usar

Quando a resposta é necessária. Se o chamador precisa do resultado, a fila adiciona complexidade sem remover a espera.

Quando o consumidor não é idempotente e não pode ser. A duplicação vai acontecer.

Quando a consistência forte é requisito. Fila implica consistência eventual.

Para um consumidor fixo e um produtor, ambos disponíveis. Chamada direta é mais simples e mais rastreável.

Sem dead-letter e sem monitoramento de profundidade. É adotar o risco sem o instrumento.

Alternativas

  • Chamada síncrona — quando a resposta importa.
  • Processamento em background no mesmo processo — para trabalho leve, sem componente adicional.
  • Tabela como fila — para volume baixo, usar o banco que já existe evita mais uma peça a operar.
  • Evento publicado — quando há vários interessados, não um consumidor. Ver arquitetura orientada a eventos.

Trade-offs

Com filaChamada direta
Produtor independe do consumidorAcoplado no tempo
Pico absorvidoPropagado
Retentativa automáticaManual
Duplicação e ordem a tratarSemântica simples
Fluxo fragmentadoRastreável
Consistência eventualTransacional
Mais um componente a operarNenhum

Modos de Falha

Consumidor não idempotente. Efeito duplicado — cobrança, e-mail, débito.

Sem dead-letter. Uma mensagem ruim trava a fila.

Confirmação antes do processamento. Perda silenciosa.

Visibilidade menor que o processamento. Duplicação sistemática.

Fila crescendo sem alerta. Descoberto pelo usuário.

Fila como banco de dados. Mensagens acumuladas para consulta posterior — não é o propósito e não tem as garantias.

Erros Comuns

Adotar sem idempotência. É o erro que produz os incidentes mais caros.

Não configurar dead-letter.

Não monitorar profundidade e idade.

Assumir ordem global.

Publicar dentro da transação sem outbox. A transação falha e a mensagem já foi publicada, ou vice-versa. Ver evento de domínio.

Exemplo Real

Um sistema de faturamento moveu a emissão de nota fiscal para uma fila, porque o serviço da prefeitura era instável e derrubava a confirmação de pedido.

Funcionou por três semanas. Depois, três incidentes em sequência.

Notas duplicadas. O serviço da prefeitura demorava mais que o tempo de visibilidade configurado. A mensagem era reentregue enquanto ainda estava sendo processada, e duas notas eram emitidas para o mesmo pedido. Corrigir exigiu cancelamento junto à prefeitura, um por um.

Fila travada. Um pedido com caractere inválido no endereço fazia o consumidor lançar exceção. A mensagem voltava para a fila e era reprocessada indefinidamente, consumindo todo o consumidor. Nove horas de emissões paradas até alguém investigar.

Acúmulo silencioso. A prefeitura ficou fora por um fim de semana. A fila cresceu para 40 mil mensagens. Ninguém percebeu porque não havia alerta — o problema apareceu na segunda-feira, quando o prazo legal de algumas notas estava próximo.

As correções, todas conhecidas de antemão e nenhuma implementada:

Chave de idempotência por pedido, verificada antes de emitir. O tempo de visibilidade subiu para o dobro do percentil 99 do processamento.

Dead-letter após três tentativas, com alerta. A mensagem problemática sai da fila em minutos, e as demais seguem.

Alerta de profundidade acima de mil e de idade da mensagem mais antiga acima de 15 minutos.

A fila estava certa como decisão. O que faltou foi tratar as três garantias antes de subir — e todas as três estavam na documentação do serviço de fila.

Conceitos Relacionados

Exercício Prático

Para cada fila do seu sistema, responda: o consumidor é idempotente? Existe dead-letter configurada? O tempo de visibilidade é maior que o pior tempo de processamento? Existe alerta de profundidade?

Cada "não" é um dos três incidentes acima esperando acontecer.

Perguntas de Entrevista

  • Quais garantias uma fila obriga a tratar?
  • Por que o consumidor precisa ser idempotente?
  • O que acontece sem dead-letter queue?

Para Aprofundar

  • Hohpe, Gregor; Woolf, Bobby. Enterprise Integration Patterns, 2003.
  • Kleppmann, Martin. Designing Data-Intensive Applications. O'Reilly, 2017.
Terminou de ler este documento?Seu progresso fica salvo apenas neste navegador.