Por que Não Usamos 2PC — E o que Fazemos em Vez Disso
O Two-Phase Commit é a resposta dos livros didáticos para transações distribuídas. Ele também está quase sempre errado para microsserviços. Aqui está o motivo — e como funciona a alternativa prática.
O que é 2PC na Prática
Two-Phase Commit (2PC) é um protocolo projetado para coordenar uma transação distribuída entre múltiplos participantes, de forma que todos façam commit ou todos façam rollback — atomicamente.
Fase 1 — Prepare: O coordenador envia "você consegue fazer commit?" para cada participante. Cada participante bloqueia seus recursos, escreve no log de transação e responde "sim" ou "não."
Fase 2 — Commit ou Rollback: Se todos os participantes disseram sim, o coordenador envia "commit." Se algum disse não (ou não respondeu), o coordenador envia "rollback." Cada participante libera seus locks e aplica o resultado.
Em teoria, isso garante commits atômicos entre múltiplos bancos de dados. Na prática, resulta em um sistema distribuído que falha de maneiras criativas.
Por que 2PC Não Funciona em Microsserviços
É um protocolo bloqueante. Durante a Fase 1, cada participante mantém locks nos seus recursos. Se o coordenador travar entre a Fase 1 e a Fase 2, esses locks ficam retidos — potencialmente indefinidamente. Todos os participantes ficam esperando por um coordenador que pode nunca voltar.
O coordenador é um Ponto Único de Falha. Em sistemas distribuídos, trabalhamos duro para eliminar SPOFs. O 2PC introduz um por design. Se o nó coordenador falhar no momento errado, toda a transação fica travada.
Partições de rede o destroem. Se um participante não consegue alcançar o coordenador durante a Fase 2, ele se comprometeu a dizer "sim" na Fase 1 mas não sabe se deve fazer commit ou rollback. Ele precisa esperar. Esse é exatamente o cenário que sistemas distribuídos precisam tratar graciosamente — e o 2PC trata bloqueando.
Sistemas heterogêneos não implementam XA. O padrão XA para transações distribuídas exige que cada participante implemente uma interface específica. Seu banco PostgreSQL suporta XA. A API REST do seu provedor de pagamento não. No momento em que você precisa coordenar uma transação que cruza uma fronteira HTTP, o 2PC fica fora de questão.
O Problema do Sucesso Fantasma
Mesmo que você pudesse usar 2PC em todo lugar, ele não resolveria o problema real dos microsserviços modernos.
Considere: seu serviço de pedidos chama a API do Stripe para cobrar um cartão de crédito. O Stripe processa a cobrança com sucesso. Então seu serviço trava antes de conseguir escrever o resultado no próprio banco de dados. Não há um coordenador 2PC entre você e o Stripe — nenhuma sessão XA, nenhum log de transação compartilhado.
O dinheiro foi cobrado. Seu banco de dados não sabe. Essa lacuna — entre um efeito colateral externo e o seu estado local — é onde o problema real mora. O 2PC não consegue atravessar essa fronteira.
Isso não é um caso de borda. Toda chamada a uma API REST externa é esse problema.
O que o Sagaweaw Faz em Vez Disso
O Sagaweaw usa o padrão Saga com compensação — a alternativa prática que realmente funciona entre fronteiras HTTP, sistemas heterogêneos e falhas de rede.
A ideia central é: em vez de uma grande transação atômica, você decompõe o fluxo de negócio em uma sequência de transações locais. Cada passo é uma operação independente, localmente atômica. Se um passo tem sucesso, você avança para o próximo. Se um passo falha, você executa transações compensatórias para desfazer o que já havia sido feito.
Nenhum lock é mantido entre serviços. Nenhum coordenador bloqueia durante uma partição de rede. Cada participante pode falhar e reiniciar de forma independente.
reservar-estoque → cobrar-pagamento → agendar-envio
↓ ↓ ↓
liberar-reserva ← estornar-pagamento ← cancelar-envio
(compensação) (compensação) (compensação)
As compensações rodam em ordem inversa. Se agendar-envio falha, você estorna o pagamento e libera a reserva. Cada compensação é em si mesma uma transação ACID local.
O Trade-off: Consistência Eventual
Isso não é de graça. Sagas são eventualmente consistentes, não imediatamente consistentes.
Durante a execução, seu sistema está em um estado intermediário. Entre cobrar-pagamento concluir e agendar-envio começar, há um breve período em que o dinheiro foi retirado mas nenhum envio foi criado. Isso é real. Isso é aceitável para fluxos de negócio.
Não é aceitável para todos os sistemas. Ledgers financeiros, relatórios regulatórios, contabilidade por partidas dobradas — esses domínios exigem consistência imediata e são genuinamente difíceis de modelar como sagas. Conheça seu caso de uso antes de escolher.
O Step Pivot
Nem todos os steps de uma saga são iguais. Normalmente existe um ponto sem retorno — um step após o qual a compensação não é mais possível de forma prática.
Em um fluxo de pagamento, esse step pode ser "fundos transmitidos ao BACEN" (Banco Central do Brasil). Uma vez que isso acontece, você não pode simplesmente chamar uma transação compensatória. O dinheiro está no sistema financeiro. A recuperação exige um processo diferente — um estorno manual, um ticket de conciliação, uma decisão humana.
O Sagaweaw modela isso explicitamente. Você pode marcar um step como PIVOT:
.step("transmitir-ao-bacen")
.type(StepType.PIVOT)
.action(bacenGateway::transmit)
[!WARNING] Steps após um PIVOT são não-compensáveis. Se eles falharem, o Sagaweaw registra a falha mas não consegue desfazer o PIVOT automaticamente. Sua equipe de operações precisa ser notificada.
Isso não é uma limitação — é um reconhecimento explícito da realidade do negócio. Algumas coisas, uma vez feitas, não podem ser desfeitas apenas por software.
A Conclusão
O 2PC é a resposta certa para um mundo onde todos os participantes compartilham a mesma infraestrutura transacional. Esse mundo não descreve os microsserviços modernos.
O padrão Saga com compensação é a resposta certa para o mundo em que realmente vivemos: serviços comunicando-se via HTTP, APIs de terceiros sem suporte a XA, bancos de dados que pertencem a times diferentes, e modos de falha que são a regra, não a exceção.
Junte-se ao debate!
Arquitetura é feita de trade-offs. O que você achou das decisões tomadas em "Por que Não Usamos 2PC — E o que Fazemos em Vez Disso"? Compartilhe seus cenários, tire dúvidas e debata com outros engenheiros da comunidade Sagaweaw.
Comentar no GitHub Discussions