Sagas e compensações: gerenciando transações distribuídas sem two-phase commit
1. Fundamentos das Transações Distribuídas e o Problema do 2PC
1.1. O que são transações distribuídas e por que o two-phase commit (2PC) é frágil
Transações distribuídas envolvem operações que afetam múltiplos bancos de dados, serviços ou sistemas de forma atômica. O protocolo two-phase commit (2PC) foi a abordagem clássica para garantir essa atomicidade: um coordenador pergunta a todos os participantes se podem confirmar (fase prepare) e, se todos responderem "sim", envia o comando de commit (fase commit). Na prática, porém, o 2PC é frágil porque depende de um coordenador central que se torna um ponto único de falha e de bloqueio.
1.2. Limitações do 2PC: bloqueio de recursos, falta de resiliência e escalabilidade
O 2PC mantém recursos bloqueados durante toda a transação — se um participante demorar a responder, todos os outros ficam esperando. Em sistemas distribuídos modernos, com alta latência de rede e falhas frequentes, isso degrada severamente o desempenho. Além disso, se o coordenador falhar após a fase prepare, os participantes podem ficar bloqueados indefinidamente (cenário de "transação órfã").
1.3. Cenário típico: microsserviços e a necessidade de consistência eventual
Em arquiteturas de microsserviços, cada serviço gerencia seu próprio banco de dados. Uma operação de negócio (como criar um pedido) pode envolver serviços de pedido, pagamento, estoque e envio. Nesse contexto, o 2PC é inviável por exigir bloqueios distribuídos. A solução é adotar consistência eventual através do padrão Saga.
2. O Padrão Saga: Conceitos e Arquitetura
2.1. Definição de Saga: sequência de transações locais com ações compensatórias
Uma Saga é uma sequência de transações locais, cada uma executada por um serviço. Se uma transação falhar, a Saga executa ações compensatórias para desfazer as transações anteriores. Diferente do 2PC, não há bloqueio global — cada transação local é confirmada imediatamente.
2.2. Diferença entre Sagas coreografadas e orquestradas
Existem duas abordagens principais:
- Coreografada: cada serviço reage a eventos e publica novos eventos, sem coordenador central.
- Orquestrada: um orquestrador central gerencia o fluxo, enviando comandos e tratando respostas.
2.3. Exemplo prático: fluxo de pedido, pagamento e estoque em uma Saga
Considere um sistema de e-commerce com três serviços: Pedido, Pagamento e Estoque. O fluxo normal é:
1. Pedido: criar pedido (status: pendente)
2. Pagamento: processar pagamento
3. Estoque: reservar itens
4. Pedido: confirmar pedido
Se o passo 3 falhar, a Saga precisa compensar: estornar pagamento e cancelar pedido.
3. Sagas Coreografadas: Comunicação Descentralizada
3.1. Funcionamento: eventos assíncronos entre serviços (event-driven)
Cada serviço publica eventos quando completa sua transação local. Serviços inscritos reagem a esses eventos e executam sua parte. Se uma falha ocorre, um evento de falha é publicado, iniciando compensações.
Exemplo de fluxo coreografado:
Serviço Pedido:
1. Cria pedido (status: pendente)
2. Publica evento: PedidoCriado
Serviço Pagamento (inscrito em PedidoCriado):
1. Processa pagamento
2. Publica evento: PagamentoProcessado
Serviço Estoque (inscrito em PagamentoProcessado):
1. Reserva itens
2. Se sucesso: publica EstoqueReservado
3. Se falha: publica EstoqueFalhou
Serviço Pedido (inscrito em EstoqueReservado):
1. Confirma pedido (status: confirmado)
Serviço Pedido (inscrito em EstoqueFalhou):
1. Publica evento: CompensarPagamento
3.2. Vantagens: acoplamento fraco, simplicidade de implementação
Serviços não precisam conhecer uns aos outros diretamente — apenas os eventos. Isso facilita a adição de novos serviços (ex: serviço de frete) sem modificar os existentes.
3.3. Desafios: rastreabilidade, tratamento de falhas e complexidade em loops
Em fluxos complexos, pode ser difícil rastrear o estado atual da Saga. Além disso, eventos de falha podem causar loops se não houver controle de idempotência. Por exemplo, se um serviço falha ao processar um evento de compensação, ele pode republicar o mesmo evento infinitamente.
4. Sagas Orquestradas: Controle Centralizado com um Orquestrador
4.1. Papel do orquestrador: gerenciar o fluxo, estados e decisões de compensação
O orquestrador é um serviço dedicado que controla toda a Saga. Ele envia comandos para cada serviço e decide, com base nas respostas, qual o próximo passo ou se deve iniciar compensações.
4.2. Implementação com máquina de estados (State Machine)
O orquestrador mantém uma máquina de estados que define os estados possíveis e as transições. Exemplo:
Estado Inicial: PEDIDO_CRIADO
Transições:
- PEDIDO_CRIADO + pagamento_ok → PAGAMENTO_OK
- PEDIDO_CRIADO + pagamento_falhou → COMPENSAR_PEDIDO
- PAGAMENTO_OK + estoque_ok → ESTOQUE_OK
- PAGAMENTO_OK + estoque_falhou → COMPENSAR_PAGAMENTO
- ESTOQUE_OK + envio_ok → CONCLUIDO
- ESTOQUE_OK + envio_falhou → COMPENSAR_ESTOQUE
Estados de compensação:
- COMPENSAR_PEDIDO → cancelar pedido → FALHA
- COMPENSAR_PAGAMENTO → estornar pagamento → FALHA
- COMPENSAR_ESTOQUE → liberar estoque → FALHA
4.3. Comparação com coreografada: quando escolher cada abordagem
| Característica | Coreografada | Orquestrada |
|---|---|---|
| Acoplamento | Fraco | Moderado |
| Complexidade de fluxos | Alta em fluxos complexos | Gerenciável |
| Rastreabilidade | Difícil | Fácil (estados centralizados) |
| Performance | Boa (assíncrona) | Pode ter latência adicional |
| Manutenção | Difícil com muitos eventos | Mais simples |
Escolha coreografada para fluxos simples e times pequenos; orquestrada para fluxos críticos com muitos serviços.
5. Ações Compensatórias: Como Desfazer Transações Parciais
5.1. Princípios de compensação: idempotência, reversibilidade e efeitos colaterais
Uma ação compensatória deve ser:
- Idempotente: executar a compensação múltiplas vezes produz o mesmo resultado.
- Reversível: deve desfazer exatamente o que a transação original fez.
- Consciente de efeitos colaterais: se a transação original gerou um e-mail, a compensação pode precisar enviar outro e-mail de cancelamento.
5.2. Exemplos de compensações: estorno de pagamento, reversão de estoque, cancelamento de envio
Transação original: ProcessarPagamento(100.00)
Compensação: EstornarPagamento(100.00) — idempotente se o sistema já verificar se o pagamento foi estornado
Transação original: ReservarEstoque(produto_X, 5)
Compensação: LiberarEstoque(produto_X, 5) — verificar se a reserva ainda existe
Transação original: EnviarPedido(pedido_123)
Compensação: CancelarEnvio(pedido_123) — pode falhar se já foi entregue
5.3. Tratamento de falhas nas próprias compensações (compensation failure)
Se uma compensação falha, o sistema precisa de estratégias adicionais:
- Retry: tentar novamente com backoff exponencial.
- Dead letter queue: registrar a falha para intervenção manual.
- Compensação alternativa: se estornar cartão de crédito falha, tentar reembolso em voucher.
6. Garantindo Consistência e Resiliência em Sagas
6.1. Estratégias de persistência de estado (logs de saga, event sourcing)
Cada passo da Saga deve ser registrado em um log persistente. No caso de falha do orquestrador, ele pode consultar o log e retomar de onde parou. Event sourcing é uma técnica complementar: armazena todos os eventos como fonte da verdade.
6.2. Mecanismos de retry, timeouts e dead letter queues
- Retry: tentar novamente operações que falharam por causas transitórias (rede, timeout).
- Timeouts: definir um tempo máximo para cada passo; se excedido, tratar como falha e iniciar compensação.
- Dead letter queues: mensagens que não puderam ser processadas após múltiplas tentativas são enviadas para uma fila separada para análise manual.
6.3. Monitoramento e recuperação: como lidar com sagas pendentes ou inconsistentes
Monitore métricas como:
- Número de Sagas em andamento
- Tempo médio de conclusão
- Sagas em estado de compensação
- Sagas que excederam timeout
Crie dashboards e alertas para Sagas que ficam pendentes por muito tempo. Considere um job de reconciliação que verifica periodicamente Sagas inconsistentes e tenta completá-las ou compensá-las.
7. Comparação Final: Sagas vs. Two-Phase Commit
7.1. Tabela comparativa: disponibilidade, desempenho, complexidade e consistência
| Característica | Sagas | Two-Phase Commit |
|---|---|---|
| Disponibilidade | Alta (falhas parciais são tratadas) | Baixa (coordenador é ponto único) |
| Desempenho | Alto (sem bloqueios) | Baixo (bloqueio de recursos) |
| Complexidade | Média (compensações) | Alta (protocolo complexo) |
| Consistência | Eventual | Forte (imediata) |
| Escalabilidade | Alta (assíncrona) | Baixa (síncrona) |
| Tolerância a falhas | Alta (compensações) | Baixa (falha do coordenador) |
7.2. Quando usar Sagas (alta escalabilidade, sistemas fracamente acoplados) vs. 2PC (consistência forte, sistemas legados)
Use Sagas quando:
- Seu sistema tem alta escalabilidade e disponibilidade
- Você pode aceitar consistência eventual (por alguns segundos/minutos)
- Os serviços são fracamente acoplados e independentes
Use 2PC (ou variantes como XA) quando:
- A consistência forte é obrigatória (ex: sistemas financeiros tradicionais)
- O sistema é monolítico ou tem poucos nós
- A latência não é crítica e o volume de transações é baixo
7.3. Padrões híbridos: combinação de Sagas com compensações e transações locais
Na prática, muitos sistemas combinam abordagens:
- Use transações locais dentro de um serviço (ex: ACID em um banco relacional).
- Use Sagas para coordenar entre serviços.
- Se necessário, use 2PC apenas para pares críticos (ex: pagamento e contabilidade) enquanto o restante usa Sagas.
Exemplo híbrido:
Serviço de Pedido:
1. Transação local: criar pedido no banco (ACID)
2. Publica evento: PedidoCriado
Serviço de Pagamento:
1. Transação local: debitar cartão (ACID)
2. Publica evento: PagamentoProcessado
Serviço de Estoque:
1. Transação local: reservar itens (ACID)
2. Se falha: publica EstoqueFalhou
Serviço de Pagamento (inscrito em EstoqueFalhou):
1. Transação local: estornar cartão (ACID)
2. Publica evento: PagamentoEstornado
Referências
- Microservices.io - Saga Pattern — Descrição detalhada do padrão Saga, incluindo coreografado e orquestrado, com exemplos de código.
- Microsoft Azure - Saga Distributed Transactions Pattern — Documentação oficial da Microsoft sobre implementação de Sagas em nuvem, com diagramas e boas práticas.
- AWS - Implementing the Saga Pattern — Guia da AWS sobre como implementar Sagas usando serviços como Step Functions e SQS.
- Martin Fowler - Sagas — Artigo conceitual de Martin Fowler sobre o padrão Saga e sua relação com transações distribuídas.
- Uber Engineering - The Saga Pattern in Distributed Transactions — Estudo de caso da Uber sobre como implementaram Sagas em larga escala para gerenciar transações de corridas.
- Red Hat - Saga Pattern for Microservices — Explicação do padrão Saga com foco em microserviços e containers, incluindo exemplos com Kubernetes.