System Design/02 - Componentes/Asynchronism2 min
Message Queues
- Que problema isso resolve?
- Quando usar / quando NÃO usar?
- Qual o principal trade-off?
- Como isso falha em produção?
Conceito
Um buffer durável entre produtor e consumidor. O produtor publica e segue; o consumidor lê quando pode.
Garantias de entrega
| Garantia | Significa | Custo |
|---|---|---|
| At-most-once | 0 ou 1 vez — pode perder | Nenhum |
| At-least-once | 1 ou N vezes — pode duplicar | Exige Idempotent Operations |
| Exactly-once | Exatamente 1 vez | Caro; quase sempre é at-least-once + dedup |
Na prática, quase todo sistema usa at-least-once + consumidor idempotente.
Modelos
| Modelo | Quem recebe |
|---|---|
| Point-to-point (fila) | Um consumidor por mensagem — trabalho dividido |
| Pub/sub (tópico) | Todos os inscritos (Publisher-Subscriber) |
Mecânica essencial
- ACK explícito: a mensagem só some depois da confirmação. Sem ACK, ela reaparece.
- Visibility timeout / lease: janela em que a mensagem fica invisível para outros. Curta demais → processamento duplicado.
- DLQ: após N tentativas, isola a mensagem venenosa para não travar a fila.
- Retenção: por quanto tempo a mensagem sobrevive não consumida.
Broker vs log
| Broker (RabbitMQ, SQS) | Log distribuído (Kafka) | |
|---|---|---|
| Após consumir | Mensagem some | Permanece até expirar |
| Replay | Não | Sim |
| Consumidores independentes | Concorrem | Cada um com seu offset |
| Ordem | Por fila | Por partição |
Trade-offs
Fila adiciona latência e complexidade em troca de resiliência e desacoplamento. A resposta deixa de ser síncrona, e o sistema precisa devolver o resultado depois (Returning Results).
Ordenação é o trade-off mais caro: garantir ordem global serializa o processamento e mata o paralelismo. A saída usual é ordenar por chave — ordem estrita dentro do cliente, paralelismo entre clientes.
E o já dito: fila mascara subcapacidade. Monitorar tamanho e idade da fila importa mais que CPU do consumidor — é a métrica que revela o déficit antes do estouro.
Exemplo prático
Upload de vídeo. A API grava o arquivo, publica {videoId} na fila e responde 202 Accepted em 200 ms. Um pool de workers consome e transcodifica, o que leva minutos.
O que a fila comprou: o usuário não espera; um pico de mil uploads só engorda a fila; se um worker morre no meio, a mensagem volta e outro pega.
O que ela cobrou: o cliente precisa descobrir quando terminou, o worker precisa ser idempotente (a mensagem pode voltar depois de já ter transcodificado), e alguém precisa vigiar a fila para notar quando ela cresce sem parar.
Relacionado
Parte de Asynchronism · roadmap.sh/system-design