Consistência · Intermediário

Transactional Inbox

Grava o efeito de negócio e o identificador da mensagem na mesma transação local.

Como ler a simulação

Os controles percorrem eventos já definidos. O relógio da tela só regula a velocidade visual. A sequência não depende de um Kafka real nem de temporizadores para decidir o resultado.

Ative uma falha para trocar o roteiro inteiro. A troca recomeça a execução, para que o estado mostrado continue correspondente ao cenário escolhido.

1 Cenário

Desenho e falha

Com o padrão ou sem ele. A escolha mostra o problema e o que queremos melhorar. Uma falha troca o roteiro inteiro.

2 Percurso

A ordem dos eventos

A trilha mostra cada etapa, inteira, na mesma tela. O relógio só muda a velocidade, não o resultado.

3 Agora

O que esta etapa faz

O texto ao lado do diagrama explica o evento que está na tela.

4 Registros

O que ficou gravado

As tabelas provam o efeito: pedido, mensagem, cobrança ou ack.

1 Cenário

Arquitetura

Problema

A cobrança não lembra a mensagem. Uma reentrega repete o efeito, e um ack antes da gravação pode perder a cobrança.

O que queremos melhorarEm tela

Gravar a cobrança e o identificador na mesma transação, e confirmar o broker só depois. A entrega repetida continua possível.

Falha

2 Percurso

Pronta para iniciar0%

Eventos

Aguardando a mensagem

Controles da simulação
VelocidadeVelocidade

Cada etapa automática leva 1,6 s. Espaço inicia ou pausa. As setas mudam a etapa.

  • Kafka: em espera
  • Billing: em espera
  • Banco de dados: em espera
  • Outro consumidor: em espera

4 Registros

Charges

CobrançaPedidoEstado
Nenhuma linha

InboxMessages

MensagemEventoEstado
Nenhuma linha

Entrega

MensagemAck
Nenhuma linha

Limite do padrãoEsta é uma simulação no navegador. A Inbox grava o identificador da mensagem junto com o efeito. Ela não torna o broker exatamente uma vez.

Problema

Um consumidor recebe a mesma mensagem mais de uma vez. Se cada entrega aplica o efeito de novo, o pedido gera duas cobranças, dois e-mails ou dois débitos.

O broker entrega ao menos uma vez. A janela entre gravar o efeito e confirmar o offset é o lugar em que a repetição aparece.

Antes e depois

Sem Inbox, gravar a cobrança e confirmar o Kafka são operações separadas. Uma reentrega repete a cobrança, porque nada lembra o identificador da mensagem. A ordem inversa confirma o broker antes do efeito e a cobrança pode se perder.

Com Inbox, a cobrança e o identificador entram na mesma transação local. O ack sai depois do commit. A próxima entrega encontra a mensagem e não repete o efeito.

Prós e contras

Benefícios

  • Uma entrega repetida não repete o efeito já confirmado.
  • A queda entre o commit e o ack deixa a cobrança única.
  • Cobrança e identificador são revertidos juntos quando a transação local falha.

Custos

  • A tabela de inbox precisa de retenção, chave única e observação.
  • Dois consumidores exigem que a inserção da chave seja a reserva, não uma consulta anterior.
  • O padrão não recupera um efeito que nunca foi gravado porque o ack saiu antes.

Quando usar

Use o padrão quando o efeito do consumidor não pode acontecer duas vezes e o broker pode entregar a mensagem de novo.

Vale a pena

  • O efeito e o registro da mensagem precisam ser aceitos ou recusados juntos.
  • O identificador da mensagem, ou uma chave de negócio, cabe numa restrição única.
  • O time consegue investigar mensagens retidas sem marcá-las como processadas.

Pode ficar de fora

  • O efeito já é naturalmente idempotente e repetir a operação não muda o resultado.
  • A operação é uma leitura, sem efeito para proteger.
  • O consumidor confirma o broker antes de saber se o efeito foi gravado.

Exemplo em .NET

A cobrança e a inbox usam a mesma transação. A consulta antes do INSERT é intencionalmente incompleta: em produção, a chave única da mensagem precisa recusar a segunda inserção. O ack continua fora da transação. O consumidor sem inbox mostra o que falta.

HandleOrderCreated.cs
// Fragmento ilustrativo. Charges e InboxMessages usam o mesmo DbContext
// e a mesma transação do banco. O ack do broker fica fora dessa transação.

await using var transaction =
    await dbContext.Database.BeginTransactionAsync(cancellationToken);

var seen = await dbContext.InboxMessages
    .AnyAsync(message => message.Id == orderCreated.MessageId, cancellationToken);

if (!seen)
{
    dbContext.InboxMessages.Add(
        InboxMessage.Create(orderCreated.MessageId, "OrderCreated"));
    dbContext.Charges.Add(Charge.For(orderCreated.OrderId));
    await dbContext.SaveChangesAsync(cancellationToken);
    await transaction.CommitAsync(cancellationToken);
}

await consumer.AcknowledgeAsync(orderCreated.MessageId, cancellationToken);
NaiveChargeHandler.cs
// Consumidor sem inbox, incompleto de propósito.
// Esta versão não guarda o identificador da mensagem.
// Se o processo parar antes do ack, a reentrega cria outra cobrança.
// Se o ack sair antes do commit, o efeito pode se perder.

dbContext.Charges.Add(Charge.For(orderCreated.OrderId));
await dbContext.SaveChangesAsync(cancellationToken);
await consumer.AcknowledgeAsync(orderCreated.MessageId, cancellationToken);

O que a Inbox não garante

  • Não transforma a entrega do broker em exatamente uma vez.
  • O ack depois do commit ainda pode acontecer mais de uma vez. A inbox é o que impede o segundo efeito.
  • Confirmar o broker antes do commit pode perder o efeito. A inbox não corrige essa ordem.
  • Consultar a inbox e inserir em outro comando abre corrida entre consumidores. A reserva é a inserção única.
  • Uma mensagem inválida não deve ser marcada como processada. Senão o efeito ausente parece concluído.
  • A retenção das linhas, o nome das tabelas e o limite de três tentativas são escolhas didáticas.

Exercícios

Responda com o que a simulação mostrou. Cada questão abre a própria resposta.

  1. 1A cobrança e a inbox já foram confirmadas, mas o processo parou antes de enviar o ack. O Kafka entrega a mesma mensagem outra vez. O que o consumidor deve fazer?Ver respostaOcultar

    Resposta

    Encontrar msg-1042 na inbox e não criar outra cobrança. Depois pode confirmar o offset. A repetição veio do broker; o efeito já está protegido pela transação local.

  2. 2O consumidor confirma o Kafka e só depois tenta gravar a cobrança. O processo cai no meio. A inbox recupera essa cobrança?Ver respostaOcultar

    Resposta

    Não. O ack já saiu e o efeito não existe. A inbox não recupera um efeito que nunca foi gravado. A ordem segura grava a cobrança e o identificador, e só então confirma o broker.

  3. 3Dois consumidores consultam a inbox, não encontram a mensagem e os dois inserem. O que faltou na reserva?Ver respostaOcultar

    Resposta

    A reserva precisa ser a própria inserção única, com restrição de chave. Consultar e inserir em comandos separados abre corrida.

  4. 4Uma mensagem inválida falha três vezes. Ela deve ser marcada como processada para sair da fila?Ver respostaOcultar

    Resposta

    Não. Marcar como processada faz o efeito ausente parecer concluído. A mensagem fica em falha, sem a cobrança.

  5. 5A inbox torna a entrega do Kafka exatamente uma vez?Ver respostaOcultar

    Resposta

    Não. O broker continua entregando ao menos uma vez. A inbox impede que a segunda entrega repita o efeito já confirmado.