Saga pattern: transações distribuídas
1. Introdução ao Problema das Transações Distribuídas
1.1. Desafios de consistência em microsserviços
Em arquiteturas monolíticas, uma única transação de banco de dados garante atomicidade, consistência, isolamento e durabilidade (ACID). Quando migramos para microsserviços, cada serviço possui seu próprio banco de dados, rompendo a capacidade de realizar transações distribuídas tradicionais. Uma operação de negócio — como criar um pedido, reservar estoque e processar pagamento — agora atravessa múltiplos serviços, cada um com seu estado independente.
1.2. Limitações do modelo ACID em ambientes distribuídos
O Two-Phase Commit (2PC) foi a tentativa clássica de estender ACID para sistemas distribuídos, mas apresenta problemas severos: bloqueio de recursos durante a fase de preparação, ponto único de falha no coordenador, e baixa escalabilidade. Em sistemas de alta disponibilidade, o 2PC se torna inviável. É nesse contexto que surge o padrão Saga.
1.3. O que é uma Saga: conceito fundamental e origem
O termo "Saga" foi introduzido por Hector Garcia-Molina e Kenneth Salem em 1987. Uma Saga é uma sequência de transações locais onde cada transação publica um evento que dispara a próxima transação. Se uma transação falha, a Saga executa transações compensatórias para desfazer o que já foi feito. Não há atomicidade global, mas sim consistência eventual.
Exemplo conceitual de Saga:
Passo 1: Serviço de Pedidos cria pedido (status: pendente)
Passo 2: Serviço de Estoque reserva itens
Passo 3: Serviço de Pagamento processa cobrança
Passo 4: Serviço de Pedidos confirma pedido (status: confirmado)
Falha no Passo 3:
- Compensação Passo 2: Serviço de Estoque libera reserva
- Compensação Passo 1: Serviço de Pedidos cancela pedido (status: cancelado)
2. Coreografia vs Orquestração: Dois Modelos de Saga
2.1. Coreografia: eventos descentralizados e acoplamento fraco
Na coreografia, cada serviço reage a eventos emitidos por outros serviços. Não há coordenador central. Os serviços se comunicam exclusivamente via message broker.
Exemplo de coreografia (fluxo textual):
1. Serviço de Pedidos publica evento "PedidoCriado"
2. Serviço de Estoque consome "PedidoCriado", reserva itens, publica "EstoqueReservado"
3. Serviço de Pagamento consome "EstoqueReservado", processa pagamento, publica "PagamentoAprovado"
4. Serviço de Pedidos consome "PagamentoAprovado", atualiza pedido para confirmado
Vantagens: acoplamento fraco, escalabilidade horizontal, simplicidade inicial. Desvantagens: difícil rastrear fluxos complexos, lógica de negócio espalhada, risco de loops de eventos.
2.2. Orquestração: um coordenador central (Saga Orchestrator)
Na orquestração, um serviço dedicado (orquestrador) gerencia o fluxo da Saga, chamando cada serviço via comando e tratando respostas.
Exemplo de orquestração (fluxo textual):
1. Orquestrador chama Serviço de Pedidos: "criarPedido()" → resposta: "pedidoId=123"
2. Orquestrador chama Serviço de Estoque: "reservarEstoque(pedidoId=123)" → resposta: "ok"
3. Orquestrador chama Serviço de Pagamento: "processarPagamento(pedidoId=123)" → resposta: "falha"
4. Orquestrador chama Serviço de Estoque: "liberarEstoque(pedidoId=123)" → compensação
5. Orquestrador chama Serviço de Pedidos: "cancelarPedido(pedidoId=123)" → compensação
Vantagens: visibilidade total do fluxo, tratamento centralizado de falhas, lógica de negócio concentrada. Desvantagens: ponto único de falha (orquestrador), maior acoplamento, complexidade de implementação.
2.3. Trade-offs: escalabilidade, visibilidade e complexidade operacional
| Aspecto | Coreografia | Orquestração |
|---|---|---|
| Acoplamento | Baixo | Médio |
| Visibilidade | Baixa | Alta |
| Escalabilidade | Alta | Limitada pelo orquestrador |
| Complexidade | Difícil de debugar | Fácil de debugar |
| Performance | Menor latência | Maior latência |
3. Estrutura de uma Transação Saga
3.1. Transações locais e compensações: a base do padrão
Cada passo de uma Saga deve ter uma transação compensatória correspondente. A transação compensatória desfaz semanticamente o que foi feito, não necessariamente restaurando o estado anterior (ex: enviar e-mail de cancelamento não apaga o e-mail original, mas compensa o efeito).
3.2. Fluxo de sucesso: encadeamento de passos
Fluxo de sucesso em orquestração (texto):
1. SagaInicio → criarPedido(pedidoId=1, itens=[A,B])
2. SagaPasso → reservarEstoque(pedidoId=1, itens=[A,B])
3. SagaPasso → processarPagamento(pedidoId=1, valor=150.00)
4. SagaPasso → confirmarPedido(pedidoId=1)
5. SagaFim → pedidoId=1 concluído com sucesso
3.3. Fluxo de falha: ativação de transações compensatórias
Fluxo de falha em orquestração (texto):
1. SagaInicio → criarPedido(pedidoId=2, itens=[C])
2. SagaPasso → reservarEstoque(pedidoId=2, itens=[C]) → ok
3. SagaPasso → processarPagamento(pedidoId=2, valor=75.00) → FALHA (saldo insuficiente)
4. SagaCompensacao → liberarEstoque(pedidoId=2, itens=[C])
5. SagaCompensacao → cancelarPedido(pedidoId=2)
6. SagaFim → pedidoId=2 cancelado, recursos liberados
4. Garantias de Consistência e Isolamento
4.1. Consistência eventual vs consistência forte
Sagas não oferecem consistência forte. Durante a execução, o sistema pode ficar em estado intermediário inconsistente. A consistência é alcançada apenas após a conclusão bem-sucedida de todos os passos ou após a execução completa das compensações.
4.2. Níveis de isolamento em Sagas: ACD (sem Atomicidade tradicional)
Sagas oferecem ACD: Atomicidade (através de compensações), Consistência (eventual) e Durabilidade. O isolamento tradicional (I do ACID) não é garantido. Leituras sujas podem ocorrer durante a execução da Saga.
4.3. Estratégias para lidar com leituras sujas e anomalias
- Semântica de contrapartida: usar contadores ou estados provisórios
- Log de compensação: registrar cada ação para permitir rollback
- Leituras apenas de Sagas concluídas: expor dados apenas quando a Saga termina
- Bloqueio otimista: usar versões e timestamps para detectar conflitos
5. Implementação com Mensageria e Eventos
5.1. Uso de message brokers (RabbitMQ, Kafka) como espinha dorsal
Tanto coreografia quanto orquestração se beneficiam de message brokers. RabbitMQ oferece filas confiáveis com confirmação de entrega. Kafka oferece logs imutáveis e replay de eventos, ideal para auditoria.
5.2. Garantia de entrega e ordenação de mensagens
Configuração de garantia de entrega (texto):
- Produtor: confirmação de entrega (ack) habilitada
- Consumidor: processamento idempotente, confirmação manual (manual ack)
- Fila: dead letter queue para mensagens com falha repetida
- Ordenação: partição única (Kafka) ou fila FIFO (RabbitMQ)
5.3. Tratamento de duplicatas e idempotência nas ações
Cada ação deve ser idempotente: executar a mesma operação múltiplas vezes produz o mesmo resultado.
Exemplo de idempotência (texto):
- "reservarEstoque(pedidoId=3, itens=[A])" verifica se já existe reserva para pedidoId=3
- Se existe: retorna sucesso sem duplicar
- Se não existe: cria reserva e retorna sucesso
6. Monitoramento, Recuperação e Tolerância a Falhas
6.1. Logging de estado da Saga (Saga Log)
Cada passo da Saga deve ser registrado em um log persistente com status: INICIADO, SUCESSO, FALHA, COMPENSADO.
Exemplo de Saga Log (texto):
sagaId=abc123 | passo=1 | servico=pedidos | acao=criarPedido | status=SUCESSO | timestamp=2024-01-01T10:00:00Z
sagaId=abc123 | passo=2 | servico=estoque | acao=reservarEstoque | status=SUCESSO | timestamp=2024-01-01T10:00:01Z
sagaId=abc123 | passo=3 | servico=pgto | acao=processarPagamento | status=FALHA | timestamp=2024-01-01T10:00:02Z
sagaId=abc123 | passo=2c | servico=estoque | acao=liberarEstoque | status=SUCESSO | timestamp=2024-01-01T10:00:03Z
sagaId=abc123 | passo=1c | servico=pedidos | acao=cancelarPedido | status=SUCESSO | timestamp=2024-01-01T10:00:04Z
6.2. Estratégias de rollback parcial e retry
Para falhas transitórias, implementar retry com backoff exponencial. Para falhas permanentes, ativar compensações.
6.3. Sagas pendentes: detecção e resolução assíncrona
Um worker de recuperação deve periodicamente verificar Sagas com status "pendente" há mais de um tempo limite e tentar concluí-las ou compensá-las.
7. Comparação com Alternativas e Boas Práticas
7.1. Quando usar Saga vs Two-Phase Commit (2PC)
| Critério | Saga | 2PC |
|---|---|---|
| Consistência | Eventual | Forte |
| Disponibilidade | Alta | Baixa (bloqueio) |
| Latência | Baixa | Alta |
| Complexidade | Média | Alta |
| Escalabilidade | Alta | Baixa |
Use Saga quando precisar de alta disponibilidade e escalabilidade. Use 2PC apenas em cenários críticos com baixa concorrência.
7.2. Armadilhas comuns: timeouts, deadlocks e falta de compensação
- Timeouts: definir timeouts realistas para cada passo
- Deadlocks: evitar Sagas circulares ou dependências cíclicas
- Falta de compensação: toda ação deve ter uma compensação implementada
- Compensação não reversível: algumas ações (enviar e-mail) não podem ser desfeitas; planejar compensação semântica
7.3. Padrões complementares: BFF, API Gateway e Event Sourcing
- API Gateway: ponto único de entrada, pode iniciar Sagas
- BFF (Backend for Frontend): adapta Sagas para diferentes clientes
- Event Sourcing: armazena eventos como fonte de verdade, complementa Sagas com auditoria completa
8. Conclusão e Cenários de Uso Recomendados
8.1. Resumo dos prós e contras do padrão Saga
Prós: escalabilidade, alta disponibilidade, resiliência, modelagem natural para microsserviços.
Contras: consistência eventual, complexidade de compensações, necessidade de idempotência, monitoramento adicional.
8.2. Exemplos reais: e-commerce, reservas de viagem, sistemas financeiros
- E-commerce: criar pedido, reservar estoque, processar pagamento, enviar notificação
- Reservas de viagem: reservar voo, reservar hotel, alugar carro, processar pagamento
- Sistemas financeiros: transferência entre contas em bancos diferentes, com compensações em caso de falha
8.3. Checklist para adoção em projetos de microsserviços
- Cada serviço possui banco de dados próprio?
- As operações de negócio atravessam múltiplos serviços?
- A consistência eventual é aceitável?
- É possível implementar compensações para cada ação?
- As operações são idempotentes?
- Existe monitoramento para Sagas pendentes?
- A equipe tem maturidade para lidar com complexidade adicional?
Se respondeu "sim" para a maioria, o padrão Saga é adequado.
Referências
- Microservices Pattern: Saga (Chris Richardson) — Artigo original do autor do padrão, com explicação detalhada e exemplos
- Saga Pattern in Distributed Transactions (Microsoft Azure) — Documentação oficial da Microsoft sobre implementação de Sagas no Azure
- Implementing Sagas (Caitie McCaffrey) — Palestra técnica sobre desafios reais de implementação de Sagas
- Saga Pattern with Kafka (Confluent) — Guia prático de implementação de Sagas usando Apache Kafka
- Distributed Sagas: A Protocol for Coordinating Microservices (Martin Fowler) — Visão de Martin Fowler sobre Sagas em sistemas distribuídos
- Saga Orchestration vs Choreography (Red Hat) — Comparação detalhada entre os dois modelos de Saga