Implementando o Padrão Outbox com Brighter: Confiabilidade na Publicação de Eventos
Rafael Andrade

Rafael Andrade @actor-dev

About: I'm Rafael the Actor Dev and I like to talk about Actor Models, Brighter, Elixir & Design Patterns Eu me chamo Rafael o Actor Dev e eu gosto de falar sobre Actor Models, Brighter, Elixir & Design Pat

Location:
London, UK
Joined:
Jan 24, 2025

Implementando o Padrão Outbox com Brighter: Confiabilidade na Publicação de Eventos

Publish Date: May 26
1 0

Introdução

Vou iniciar uma série sobre o Brighter para explorar o padrão Outbox. Neste artigo, vamos entender como o Brighter implementa esse padrão de forma nativa, garantindo consistência transacional ao publicar mensagens junto com atualizações no banco de dados. Esse padrão é essencial em sistemas distribuídos, onde soluções como Two-Phase Commit (2PC) são inviáveis.

Contexto

Antes de mergulhar no padrão Outbox, vamos entender o problema que ele resolve:

Problema 1: Mensagem Única Após Atualização no Banco

Imagine um sistema que atualiza um registro no banco de dados (ex: PedidoCriado) e publica uma mensagem. Se o broker de mensagens falhar durante a publicação (ex: problemas de rede), a atualização no banco será bem-sucedida, mas a mensagem será perdida. Isso gera inconsistência entre o estado do sistema e os serviços dependentes.

Uma solução ingênua seria usar retentativas, mas isso pode resultar em mensagens duplicadas ou falhas parciais.

Problema 2: Múltiplas Mensagens com Requisitos de Atomicidade

À medida que o sistema cresce, você pode precisar publicar várias mensagens (ex: PedidoPago e EstoqueAtualizado) em uma única transação. Se a segunda mensagem falhar após a primeira ser publicada, os sistemas downstream receberão uma parte das mensagens, violando requisitos de entrega atômica ou idempotência.

Exemplo:

  1. Atualize um pedido (ex: pedido.Status = "Pago").
  2. Publique PedidoPago e EstoqueAtualizado.
  3. Se EstoqueAtualizado falhar após PedidoPago ser enviado, o sistema downstream terá um fluxo incompleto.

Isso destaca a necessidade de garantias transacionais ao combinar escritas no banco e publicação de mensagens.

Por Que Soluções Tradicionais Não Funcionam

  • Two-Phase Commit (2PC): A maioria dos bancos e brokers (ex: Kafka, RabbitMQ) não suporta 2PC. Mesmo que suportem, isso introduz acoplamento e overhead de performance.
  • Retentativas Manuais: Sem isolamento transacional, retentativas podem publicar mensagens duplicadas ou falhar em recuperação de falhas parciais.

Padrão Outbox

O padrão Outbox garante entrega de mensagens pelo menos uma vez (at-least-once delivery) e mantém a consistência transacional entre atualizações no banco e publicação de mensagens. Ele garante que mensagens sejam publicadas somente se a transação no banco for concluída com sucesso, evitando falhas parciais.

Como Funciona

Em vez de publicar mensagens diretamente no gateway de mensageria (ex: Kafka, RabbitMQ), o padrão armazena as mensagens em um banco transacional (ex: PostgreSQL, MySQL) dentro da mesma transação dos dados de negócio. Isso assegura:

  1. Persistência Atômica: Mensagens e dados são salvos juntos.
  2. Processo de Retransmissão: Um serviço em segundo plano (sweeper) lê mensagens do Outbox e as publica no gateway.
  3. Recuperação de Falhas: Se o sistema falhar antes da publicação, a mensagem permanece no Outbox e será reprocessada na recuperação.

outbox design

Vantagens Principais

  1. Consistência Transacional: Mensagens e atualizações no banco são persistidas na mesma transação, eliminando a necessidade de transações distribuídas.
  2. Tolerância a Falhas: Se o gateway de mensageria falhar, as mensagens são reprocessadas até serem entregues.
  3. Arquitetura Desacoplada: O sistema se concentra em transações locais; a entrega de mensagens é assíncrona.

Implementação do Outbox no Brighter

O Brighter oferece suporte nativo ao padrão Outbox para múltiplos provedores (ex: PostgreSQL, MySQL, DynamoDB).

Como Funciona o Método Post Internamente

Ao usar Post para enviar uma mensagem, o Brighter aplica o padrão Outbox em segundo plano:

  1. Deposit:

    • O método Post chama Deposit, armazenando a mensagem no Outbox dentro da mesma transação do banco de dados.
    • Garante que mensagens só sejam persistidas se a transação for concluída com sucesso.
  2. ClearOutbox:

    • Após a transação, ClearOutbox é chamado com o ID da mensagem.
    • Publica a mensagem no gateway (ex: RabbitMQ, Kafka) e a remove do Outbox após a entrega.

Configuração Padrão

Por padrão, o Brighter usa um Outbox em memória, ideal para testes mas não recomendado para produção, já que mensagens são perdidas se o sistema travar/cair antes da publicação.

Como Configurar

services
    .AddBrigther()
    .UseInMemoryOutbox() // Ou .UseExternalOutbox(...) para provedores externos
    .UseOutboxSweeper(options =>
    {
        options.TimerInterval = 5; // Intervalo entre verificações (em segundos)
        options.MinimumMessageAge = 500; // Idade mínima da mensagem (em milissegundos)
    });
Enter fullscreen mode Exit fullscreen mode

Você precisará do pacote Paramore.Brighter.Extensions.Hosting para o sweeper e Paramore.Brighter.Extensions.DependencyInjection para integrar o Brighter com a DI do Microsoft.

Conclusão

O padrão Outbox é fundamental para comunicação confiável em microsserviços. Ao usar o suporte nativo do Brighter:

  • Garante consistência transacional sem 2PC.
  • Implementa entrega at-least-once com retentativas.
  • Desacopla atualizações no banco da publicação de mensagens.

Nos próximos artigos, explorarei provedores de Outbox do Brighter (ex: PostgreSQL, DynamoDB) e configurações avançadas como deduplicação de mensagens e lógica customizada para o sweeper.

Referências

Comments 0 total

    Add comment