Menu fechado

Cloudflare Workflows: Implementando o Padrão Saga com Rollbacks

Cloudflare Workflows

🚀 Introdução ao Cloudflare Workflows e o Desafio da Consistência

O Cloudflare Workflows é uma solução projetada para simplificar a criação de aplicações complexas e de longa duração. Ele permite que desenvolvedores construam processos multi-etapas que são inerentemente duráveis, oferecendo persistência de estado e mecanismos nativos de repetição (retries) caso ocorram falhas durante a execução.

Em um fluxo de trabalho típico, cada etapa pode interagir com sistemas externos, aguardar respostas e manter o progresso mesmo após reinicializações do sistema. No entanto, a execução de processos distribuídos traz um desafio crítico: a consistência de estado. Quando um fluxo é interrompido por uma falha em uma etapa intermediária, o sistema pode ficar em um estado parcial ou inconsistente, onde algumas operações foram concluídas com sucesso, enquanto outras nunca foram iniciadas ou falharam.

O Dilema da Transação Distribuída

Para ilustrar esse desafio, considere um cenário de transferência de fundos entre dois bancos distintos:

  1. Etapa 1: Debitar o valor da conta no Banco A.
  2. Etapa 2: Creditar o valor na conta no Banco B.
  3. Etapa 3: Enviar uma confirmação por e-mail para ambos os proprietários.

O problema surge se a Etapa 2 falhar. Como a Etapa 1 já foi concluída com sucesso, o dinheiro já saiu do sistema do Banco A e a transação foi efetivada lá. Como o orquestrador do processo, você não pode simplesmente “desfazer” a operação no Banco A, pois o estado já foi alterado permanentemente. Em sistemas distribuídos, a solução não é o cancelamento simples, mas sim a execução de uma operação de compensação — um novo comando que reverte semanticamente o efeito do primeiro (por exemplo, realizando um crédito de estorno no Banco A).

Essa necessidade de emparelhar uma operação com sua lógica de compensação é a base do padrão saga. Sem uma estrutura dedicada, desenvolvedores seriam forçados a gerenciar manualmente o rastreamento de quais etapas foram concluídas e quais ações de reversão seriam necessárias, resultando em códigos complexos, propensos a erros e difíceis de manter.

🔄 O Padrão Saga e a Introdução de Rollbacks

Em sistemas distribuídos, uma vez que uma operação externa é confirmada — como o débito em uma conta bancária —, ela não pode ser simplesmente “desfeita” pelo orquestrador. Para resolver esse desafio, utilizamos o padrão Saga, uma estratégia de gerenciamento de transações de longa duração onde cada ação possui uma operação de compensação correspondente que reverte semanticamente o efeito da ação original.

Anteriormente, implementar esse padrão exigia que desenvolvedores gerenciassem manualmente o estado de sucesso ou falha de cada etapa, frequentemente recorrendo a blocos try-catch complexos e propensos a erros para garantir que as compensações fossem executadas na ordem correta. Com a nova funcionalidade de rollbacks do Cloudflare Workflows, essa lógica de compensação torna-se uma propriedade de primeira classe do próprio fluxo de trabalho.

Definição Declarativa de Compensação

Agora, você pode definir a lógica de rollback diretamente como um argumento no método step.do(). Isso garante que a durabilidade do seu workflow se estenda também à fase de reversão, mantendo o código limpo e coeso. Ao associar a compensação à definição da etapa, o Workflows assume a responsabilidade de rastrear o que foi executado e, em caso de falha terminal, disparar os manipuladores de rollback na ordem inversa à execução original.

Veja como a sintaxe simplifica a implementação:


await step.do("debit-account-a", async () => {
return await bankA.debit({ ... });
}, {
rollback: async ({ output }) => {
await bankA.credit({ ... });
},
});

Essa abordagem elimina a necessidade de lógica de rastreamento manual, pois o Workflows utiliza o histórico durável da execução para identificar quais etapas foram concluídas e quais necessitam de compensação. Além disso, ao tratar o rollback como parte da configuração da etapa, garantimos que as operações de reversão herdem as mesmas propriedades de resiliência, como retentativas automáticas e timeouts configuráveis, garantindo que o sistema retorne a um estado consistente mesmo diante de falhas parciais.

🛠️ Como Implementar Rollbacks no seu Fluxo

Para implementar a lógica de compensação, você deve passar um objeto de opções como o terceiro argumento da função step.do(). Este objeto deve conter a propriedade rollback, que recebe uma função assíncrona responsável por reverter a operação realizada no passo original.

Definindo a Lógica de Compensação

O exemplo abaixo demonstra como estruturar uma transferência bancária onde o rollback garante que o débito seja estornado caso o crédito subsequente falhe:


const debit = await step.do(
"debit-account-a",
async () => {
return await bankA.debit({
accountId: fromAccountId,
amount,
idempotencyKey: ${transferId}:debit-account-a,
});
},
{
rollback: async () => {
await bankA.credit({
accountId: fromAccountId,
amount,
idempotencyKey: ${transferId}:rollback-debit-account-a,
});
},
}
);

A Regra de Ouro: Idempotência

É fundamental que suas funções de rollback sejam idempotentes. Isso significa que, se a função for executada múltiplas vezes devido a retentativas automáticas do sistema, o resultado final no sistema externo deve ser o mesmo de uma única execução bem-sucedida.

  • Utilize Chaves de Idempotência: Sempre que possível, envie uma chave única (como um ID de transação ou um sufixo específico para o rollback) para o serviço externo. Isso impede que, por exemplo, um estorno seja processado duas vezes caso ocorra uma falha de rede durante a comunicação.
  • Tratamento de Saída (Output): O manipulador de rollback recebe um objeto contendo o output da etapa original. Lembre-se de verificar se output é undefined, pois o rollback pode ser acionado mesmo que o passo original tenha falhado antes de retornar um valor persistido.
  • Configuração de Retentativas: Você pode refinar o comportamento da compensação utilizando rollbackConfig, permitindo definir limites de retentativas, atrasos e políticas de backoff exponencial, garantindo que o seu sistema de rollback seja tão resiliente quanto o fluxo principal.

Quando um erro ocorre no fluxo, o Cloudflare Workflows identifica automaticamente quais passos foram iniciados e executa os manipuladores de rollback registrados em ordem inversa à de início, garantindo a integridade dos seus dados sem a necessidade de blocos try-catch complexos e manuais.

⚙️ Regras e Comportamento da Execução de Rollback

Para garantir a integridade dos dados e a previsibilidade em processos distribuídos, o sistema de rollback do Cloudflare Workflows segue diretrizes operacionais rigorosas:

1. Elegibilidade de etapas com falha

Uma chamada step.do() que resulta em erro ainda pode ser elegível para rollback, desde que um manipulador de rollback tenha sido registrado. Isso é fundamental porque uma etapa pode interagir parcialmente com um sistema externo antes de falhar (por exemplo, um provedor de pagamento capturando uma cobrança antes de retornar um erro de rede). Os manipuladores recebem o parâmetro output, que será undefined caso a etapa tenha falhado antes de persistir um valor, permitindo que o desenvolvedor trate a compensação de forma condicional.

2. Acionamento apenas em falhas terminais

A definição de um manipulador de rollback não altera o fluxo normal de execução. Se o código do usuário capturar um erro e o Workflow prosseguir, o rollback não será disparado. O processo de reversão é iniciado apenas quando o Workflow atinge um estado de falha terminal. Nesse momento, o motor do Workflow identifica todas as etapas elegíveis, executa seus respectivos manipuladores de compensação e, somente após a conclusão desses processos, registra o estado final de falha do Workflow.

3. Ordem de execução: Inverso da ordem de início

Diferente da ordem de conclusão, que pode variar em fluxos paralelos ou assíncronos, a ordem de rollback segue estritamente o inverso da ordem de início (reverse step-start order). Esta abordagem garante uma fonte de verdade estável baseada no histórico persistido. Independentemente de quando uma etapa terminou, o sistema garante que as operações sejam desfeitas na sequência lógica inversa à sua ativação, assegurando que dependências sejam revertidas corretamente antes de etapas anteriores.




🧠 Decisões de Design da API: Por que a Forma Explícita?

A definição da interface para os rollbacks exigiu um equilíbrio cuidadoso entre expressividade e a preservação da semântica de execução durável do Cloudflare Workflows. Durante o processo de design, avaliamos três abordagens distintas antes de consolidar a implementação atual.

A Armadilha da API Fluente (Chaining)

Consideramos inicialmente um padrão de encadeamento, como step.do(...).rollback(...). Embora essa sintaxe seja elegante e comum em JavaScript, ela introduz ambiguidades críticas. O método step.do() já retorna uma Promise que representa o resultado da etapa. Em ambientes Workers, isso é fundamental para o promise pipelining (via RPC), permitindo que operações sejam enfileiradas antes mesmo da conclusão da etapa anterior. Uma API fluente tornaria o tempo de execução da etapa imprevisível, pois o motor do Workflow precisaria aguardar para verificar se um .rollback() seria anexado antes de iniciar a operação, comprometendo a natureza assíncrona e eficiente do sistema.

O Custo do Estilo Builder

Outra alternativa seria um padrão builder, como step.saga("nome").do(...).rollback(...).run(). Embora essa abordagem eliminasse a ambiguidade das Promises, ela introduziria uma carga cognitiva e de código desnecessária (ceremony). A necessidade de um método final .run() criaria um ponto de falha comum — esquecer de chamá-lo seria um erro difícil de detectar. Além disso, essa estrutura fragmentaria a API, tratando step.do() como um método legado em vez de mantê-lo como a primitiva central do Workflows.

A Escolha pela Forma Explícita: Metadados em step.do()

A decisão final foi integrar o rollback como metadados dentro do próprio step.do(..., { rollback }). Esta escolha oferece vantagens fundamentais:

  • Coesão: A lógica de compensação reside exatamente ao lado da operação que ela reverte, facilitando a legibilidade e a manutenção.
  • Previsibilidade: Como o rollback é passado como uma opção de configuração, o motor do Workflow conhece a intenção de compensação no momento exato em que a etapa é iniciada, sem depender de encadeamentos ou estados intermediários.
  • Simplicidade de Adoção: Desenvolvedores que já utilizam o step.do() não precisam aprender um novo paradigma ou construtor; basta adicionar um objeto de configuração.
  • Modelo de Execução: Esta forma mantém a integridade do modelo de execução durável, garantindo que o rollback seja tratado como um comportamento de ciclo de vida da unidade de trabalho, e não como uma estrutura de controle externa ou um bloco try-catch global.

Ao tratar o rollback como metadado, garantimos que o sistema possa registrar a intenção de compensação no histórico durável do Workflow, permitindo que o motor recupere e execute a lógica necessária mesmo após reinicializações ou falhas do sistema, mantendo a robustez que define o Cloudflare Workflows.

💾 Bastidores: Como o Workflows Gerencia o Estado e a Recuperação

A implementação de rollbacks no Cloudflare Workflows vai além de uma simples lógica de tratamento de erros; ela exige uma integração profunda com o motor de persistência do sistema. Para garantir que a compensação ocorra mesmo após falhas catastróficas, o Workflows utiliza uma combinação de histórico durável e referências de execução.

O Papel do Histórico Durável e Stubs

O motor do Workflows mantém um registro persistente de cada passo executado. Quando um step.do() é invocado, o sistema armazena não apenas o status de conclusão e o resultado (output), mas também metadados sobre a existência de lógica de compensação. Para invocar essa lógica, o sistema utiliza stubs via Workers RPC. Um stub atua como uma referência chamável que permite ao motor disparar o código de rollback, mesmo que a execução original tenha ocorrido em um contexto diferente.

Recuperação via Replay e Reconstrução de Handlers

Em cenários onde o motor é reiniciado ou o ambiente de execução é descartado, as referências em memória (stubs) podem ser perdidas. É aqui que o mecanismo de replay se torna fundamental. O Workflows reexecuta o código do fluxo em um modo de recuperação, onde passos já concluídos não são processados novamente — o sistema apenas lê o resultado persistido no histórico.

Durante esse processo de replay, o Workflows encontra os pontos de chamada originais dos passos. Ao reprocessar essas linhas, o sistema reconstrói os stubs dos manipuladores de rollback associados, registrando-os novamente no contexto atual. Esse comportamento garante que:

  • O motor recupere a capacidade de compensação sem reexecutar os efeitos colaterais dos passos originais (como realizar um novo débito bancário).
  • A ordem de rollback seja mantida estritamente baseada no histórico de início dos passos, garantindo consistência mesmo em fluxos complexos.
  • A falha de um manipulador de rollback seja tratada com a mesma resiliência de um passo comum, respeitando as configurações de rollbackConfig como retentativas e timeouts.

Dessa forma, o Workflows garante que, independentemente de reinicializações, o sistema sempre saiba exatamente quais passos foram iniciados, quais foram concluídos e qual é a sequência correta de ações para reverter o estado do sistema para um ponto de consistência.

🔮 O Futuro e Próximos Passos

Embora a implementação atual de rollbacks ofereça uma base robusta para garantir a consistência em aplicações distribuídas, nossa jornada está apenas começando. Estamos trabalhando ativamente para expandir as capacidades desta funcionalidade com as seguintes melhorias planejadas:

Suporte a waitForEvent

Planejamos estender a lógica de compensação para eventos de longa duração, permitindo que processos que aguardam gatilhos externos também possuam mecanismos de reversão caso o fluxo seja interrompido ou falhe após a recepção do evento.

Execução Paralela de Rollbacks

Atualmente, os rollbacks são executados de forma sequencial seguindo a ordem inversa de início. Estamos explorando formas de permitir a execução paralela de compensações para cenários onde a performance é crítica e as operações de reversão são independentes entre si.

Suporte para Python Workflows

A paridade de recursos é uma prioridade. Em breve, os desenvolvedores que utilizam o SDK de Python para o Cloudflare Workflows poderão definir lógicas de rollback com a mesma sintaxe intuitiva e segurança de estado que disponibilizamos para JavaScript.

Quando uma aplicação de múltiplas etapas falha, o maior desafio não é apenas detectar o erro, mas entender o estado parcial do sistema e como restaurá-lo. As sagas de rollback permitem que você defina essa estratégia de recuperação diretamente junto à sua lógica de negócio.

Para começar a implementar essas soluções em suas aplicações, consulte a documentação oficial do Cloudflare Workflows. Adoraríamos ouvir sua opinião: compartilhe seus padrões de compensação favoritos e feedbacks na Cloudflare Community.


Fonte: blog.cloudflare.com
Curadoria e Insights: Redação YTI&W (Developers).



Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Publicado em:Cloud Computing,Desenvolvimento de Software,Engenharia de Software,Infraestrutura de TI
Fale Conosco
×

Inscreva-se em nossa Newsletter!


Receba nossos lançamentos e artigos em primera mão!