Kafka garante entrega.
Não garante processamento.
Essa distinção parece simples, mas muita arquitetura quebra exatamente por não tratá-la com a seriedade necessária.
O broker recebeu, replicou, reteve e disponibilizou a mensagem corretamente.
Ótimo.
Isso ainda não prova que seu banco foi atualizado, que a API externa respondeu, que o e-mail foi disparado, que a cobrança aconteceu uma única vez ou que o estado final do negócio ficou consistente.
Entrega e processamento não são a mesma garantia.
Quando o time mistura as duas coisas, começa a cobrar do Kafka uma responsabilidade que é do consumidor.
O que o Kafka garante de verdade
No escopo certo, o Kafka entrega bastante.
Ele sabe:
- persistir mensagens no log;
- replicar dados entre brokers;
- expor leitura ordenada dentro da partição;
- coordenar avanço de consumo por offset;
- reentregar registros quando o consumidor falha antes de consolidar sua posição.
Isso é valioso.
Foi justamente essa camada que apareceu no post sobre durabilidade e tolerância a falhas: o cluster pode estar íntegro, a mensagem pode continuar existindo e ainda assim o seu sistema falhar em produzir o efeito esperado.
O ponto central é este:
o Kafka garante transporte e armazenamento dentro do seu modelo.
Ele não observa a semântica real do que sua aplicação fez com a mensagem depois de lê-la.
O que o Kafka não consegue garantir por você
Assim que o evento sai do log e entra no seu código, a responsabilidade muda de mãos.
O Kafka não sabe se:
- a transação no banco foi concluída;
- a chamada HTTP realmente funcionou;
- o retry repetiu efeito colateral;
- o commit de offset aconteceu cedo demais;
- a regra de negócio abortou no meio;
- a aplicação caiu depois de executar metade do fluxo.
Do ponto de vista do broker, nada disso existe como verdade de negócio.
Existe leitura, commit, retenção e protocolo de consumo.
O resto está fora da fronteira que a plataforma consegue proteger.
É por isso que "a mensagem foi entregue" não deveria ser traduzido como "o processamento deu certo".
O lugar onde a confusão nasce
Imagine este fluxo:
- o consumer lê um evento de pagamento;
- grava uma auditoria no banco;
- chama um gateway externo;
- atualiza o status do pedido;
- confirma o offset.
Agora troque a ordem mental por um incidente real:
- o consumer leu a mensagem;
- parte do trabalho aconteceu;
- a aplicação falhou antes do fluxo terminar;
- o offset avançou no momento errado ou a operação externa já produziu efeito.
Nesse cenário, o Kafka não “descobre” sozinho o que faltou.
Ele só enxerga a parte que pertence ao seu protocolo.
Foi exatamente esse tipo de armadilha que apareceu nos posts sobre perda lógica no consumer, auto commit mentir e idempotência.
O broker pode estar impecável.
O processamento pode continuar errado.
Offset não é recibo de negócio
Esse ponto merece destaque isolado.
Muita equipe olha para offset commitado e imagina uma confirmação de sucesso.
Não é.
Offset commitado só significa que o consumer registrou uma posição de leitura como próxima referência para retomada.
Ele não carrega, por si só, nenhuma prova de que:
- o banco persistiu;
- o efeito externo foi concluído;
- a operação não será repetida;
- o estado final ficou consistente.
Se o time trata commit como recibo de processamento, passa a ler o sistema com a semântica errada.
E quando isso acontece, lag baixo, partição avançando e gráfico bonito viram métricas enganosas.
O consumo parece saudável.
O negócio não necessariamente está.
Até exactly-once tem fronteira
Outro ponto que costuma confundir bastante é exactly-once.
Quando bem configurado, ele ajuda muito em fluxos Kafka para Kafka.
Consumir, transformar, publicar resultado e coordenar offsets dentro da transação é uma capacidade poderosa.
Mas isso ainda não transforma banco, cache, e-mail, webhook ou gateway de pagamento em participantes nativos dessa garantia.
Foi por isso que o post sobre exactly-once insistiu tanto no escopo real da promessa.
Exactly-once não apaga a necessidade de idempotência fora do log.
Ele fortalece uma fronteira específica.
Não fecha automaticamente a conta do sistema inteiro.
O que realmente protege processamento
Se processamento importa, a proteção precisa aparecer no desenho do consumidor.
Alguns pilares costumam ser indispensáveis:
- commit no momento certo, depois do ponto de verdade do processamento;
- idempotência para tolerar reentrega e repetição;
- retry com critério, para não amplificar falha;
- DLT ou DLQ para tirar eventos insolúveis do caminho;
- observabilidade que meça efeito de negócio, não só avanço de offset;
- modelagem cuidadosa da fronteira entre Kafka e efeitos externos.
Dependendo do fluxo, isso pode incluir:
upsertem vez de insert cego;- chave de deduplicação;
- outbox;
- processamento assíncrono desacoplado;
- reconciliação posterior.
Kafka ajuda bastante.
Mas a garantia final de processamento nasce da combinação entre protocolo de consumo e desenho da aplicação.
A pergunta certa não é “a mensagem chegou?”
Essa pergunta é útil, mas é insuficiente.
A pergunta mais madura é:
"qual evidência eu tenho de que o efeito esperado aconteceu do jeito certo, no lugar certo e sem duplicidade indevida?"
Se a única resposta do sistema for "o offset avançou", a arquitetura ainda está olhando para a metade errada do problema.
O ponto que vale fixar
Kafka garante entrega dentro do modelo dele.
Quem garante processamento é o seu consumer.
Se o seu sistema precisa de efeito confiável de negócio, não basta confiar na durabilidade do broker.
É preciso desenhar explicitamente commit, idempotência, retry, tratamento de falhas e fronteiras de consistência.
Porque mensagem entregue e trabalho concluído continuam sendo coisas diferentes.
