M03·04Conceitos Críticos

CAPÍTULO 04

Conceitos Críticos

Consistência eventual, idempotência, retry com backoff exponencial e Dead Letter Queue — o quarteto que evita desastres.

Por Thiago Souza10 min de leituraAtualizado em 2026-05

Consistência Eventual

Em sistemas síncronos tradicionais (banco de dados único), quando você escreve um dado, na próxima leitura ele já está lá. Isso é consistência forte.

Em sistemas distribuídos com Kafka, o serviço A publica um evento e o serviço B vai processar em algum momento. Pode ser em 10ms, pode ser em 5 minutos se estiver com fila acumulada.

Consistência eventual significa: se você parar de escrever, eventualmente todos os nós convergem para o mesmo estado.

T=0s: Pedido criado no Serviço A. Estoque ainda não atualizado.
T=1s: Evento publicado no Kafka.
T=2s: Serviço B (Estoque) processa o evento e atualiza.
T=2s: Sistemas consistentes!

Entre T=0s e T=2s, o sistema está inconsistente — e isso é normal e esperado.

O front-end mostra "Pedido criado!" mas na tela seguinte o estoque ainda não diminuiu. Isso vai acontecer. Você precisa desenhar a UX pensando nisso (ex: mostrar "Processando...", usar webhooks, polling, etc).

Idempotência

Idempotência = você pode aplicar a operação 1, 2, 50 vezes que o resultado é o mesmo de aplicar 1 vez.

Exemplo idempotente: "definir o status do pedido 123 para PAGO". Não importa quantas vezes você executa, vai ficar PAGO.

Exemplo NÃO idempotente: "adicionar R$100 ao saldo". Executar 3 vezes credita R$300 — desastre.

Por que isso importa em Kafka? Porque mensagens podem chegar mais de uma vez. Se seus consumers não forem idempotentes, você vai cobrar o cliente 3 vezes, mandar 5 e-mails, etc.

Como tornar consumer idempotente?

Estratégia 1: Identificador único + tabela de processados

sql
CREATE TABLE eventos_processados (
  evento_id UUID PRIMARY KEY,
  processado_em TIMESTAMP
);

Antes de processar: verifica se o evento_id já existe. Se sim, ignora.

Estratégia 2: Operações naturalmente idempotentes

❌ "adicionar 100 ao saldo"  →  use upserts/sets
✅ "definir saldo = 500"

Estratégia 3: Versionamento (optimistic locking)

Só atualize se a versão atual = X. Se outro processo já atualizou, falha.

Retry — Tentar de Novo Sem Quebrar Tudo

Quando o consumer falha ao processar (ex: API externa indisponível), você precisa tentar de novo. Mas atenção:

Tipos de Erro

  • Erros transitórios (rede, timeout, 503): retentativas fazem sentido.
  • Erros permanentes (payload inválido, bug no código): retentar não vai mudar nada — vai só consumir CPU à toa.

Estratégias de Retry

Retry imediato — não faça isso:

falhou → tenta de novo na hora → falhou → tenta de novo → ...

Se a API está fora, você martela ela mais ainda.

Retry com Backoff Exponencial:

1ª tentativa: imediata
2ª tentativa: depois de 1s
3ª tentativa: depois de 2s
4ª tentativa: depois de 4s
5ª tentativa: depois de 8s
...com um teto (ex: máximo 60s)

Retry com Jitter: backoff exponencial + um pouco de aleatoriedade. Evita que mil consumers tentem ao mesmo tempo (efeito "thundering herd").

delay = min(maxDelay, base * 2^tentativa) + random(0, 1s)

Onde retentar?

  • Em memória (loop dentro do consumer): bom pra retries rápidos (1-3 tentativas).
  • Em topics de retry (retry topics): bom pra retries longos. Cada falha é jogada em outro topic com um delay.
flowchart TD
  T0["Topic: pedidos"] -->|"falhou"| T1["pedidos.retry.1m<br/>consumer espera 1min e tenta de novo"]
  T1 -->|"falhou"| T2["pedidos.retry.5m"]
  T2 -->|"falhou"| T3["pedidos.retry.30m"]
  T3 -->|"falhou"| DLQ["pedidos.dlq<br/>desistiu"]

Dead Letter Queue (DLQ)

DLQ é o cemitério das mensagens que não conseguiram ser processadas.

Depois de N tentativas, em vez de ficar tentando pra sempre (e bloqueando a fila), você joga a mensagem numa DLQ — um topic separado. Lá, um humano (ou um job) analisa o que aconteceu.

O que vai pra DLQ?

  • Payloads malformados (parsing falhou).
  • Bugs descobertos só em produção.
  • Dados que dependem de algo inexistente (ex: pedido referencia produto deletado).
Anti-pattern: ter DLQ e nunca olhar pra ela. DLQ sem alarme = lixo acumulado. Sempre coloque alertas: "se DLQ tiver mais de N mensagens, dispara um alarme".