Introdução
Como você sabe, a transição de uma arquitetura de monólito para uma arquitetura de microsserviço causa uma série de dificuldades associadas tanto à parte técnica do projeto quanto ao fator humano. Um dos desafios técnicos mais difíceis é garantir a consistência em um sistema distribuído.
Da última vez, discutimos as causas dos problemas de consistência em uma arquitetura de microsserviço, a abordagem otimista à consistência e a consistência usando um commit de duas fases.
Padrão saga
Saga é um mecanismo para garantir a consistência dos dados em uma arquitetura de microsserviço sem usar transações distribuídas.
Para cada comando do sistema que precisa atualizar dados em vários serviços, uma saga é criada. A saga é uma espécie de "lista de verificação" que consiste em transações ACID locais sequenciais, cada uma atualizando dados em um serviço. Uma transação de compensação é aplicada para lidar com as falhas. Essas transações são executadas em caso de falha em todos os serviços nos quais as transações locais foram concluídas com sucesso.
Existem vários tipos de transações na saga, até quatro:
- Compensar - Desfaz uma alteração feita por uma transação local.
- Uma compensação é uma transação que precisa ser compensada (revertida) no caso de falha nas transações subsequentes.
- Pivotal - uma transação que determina o sucesso de toda a saga. Se der certo, a saga certamente chegará ao fim.
- Repetível - segue o pivô e tem sucesso garantido.
Você pode organizar uma saga usando coreografia ou orquestração.
No caso da saga coreográfica, não há um orquestrador dedicado. Usando o serviço de pedidos e os usuários como exemplo, pode ter a seguinte aparência: o serviço de pedidos recebe uma solicitação e cria um pedido no estado PENDENTE e, em seguida, publica o evento "Pedido criado". Um manipulador de eventos no serviço de usuário processa esse evento, tenta reservar um item e publica o resultado como um evento. O serviço de pedidos processa este evento, confirmando ou cancelando o pedido dependendo do resultado lido.
A saga orquestrada parece um pouco mais interessante. Usando os serviços acima como exemplo, pode ter a seguinte aparência: o serviço de pedidos recebe uma solicitação, cria uma saga que cria um pedido no estado PENDENTE e, em seguida, envia um comando para reservar mercadorias para o serviço do usuário. O serviço ao usuário tenta reservar o produto e envia uma mensagem de resposta indicando o resultado. A saga aprova ou cancela o pedido.
O padrão saga permite que um aplicativo mantenha a consistência dos dados em vários serviços sem usar transações distribuídas (commits de duas fases) e evitando os problemas discutidos no artigo anterior. Mas, por outro lado, o modelo de programação é muito complicado: por exemplo, o desenvolvedor para cada transação deve escrever uma transação de compensação que reverta as alterações feitas anteriormente na saga.
A saga nos permite alcançar um modelo ACD (Atomicidade + Consistência + Durabilidade em termos de ACID), mas perdemos uma letra. A falta da letra I leva aos conhecidos problemas de falta de isolamento. Isso inclui: atualizações perdidas - uma saga sobrescreve as alterações feitas por outra sem lê-las, leituras sujas - uma transação ou saga lê atualizações inacabadas de outra saga, fuzzy / leituras não repetíveis) - dois estágios diferentes da saga leem os mesmos dados, mas obtêm resultados diferentes porque outra saga fez uma alteração. Existem vários padrões que permitem corrigir certas anomalias: bloqueio semântico, atualizações comutativas, representação pessimista, releitura de um valor, arquivo de alterações e por valor.A questão de garantir o isolamento permanece em aberto.
Outro problema interessante é a impossibilidade de atualizar atômica o banco de dados e postar uma mensagem para o corretor de mensagens para acionar outras etapas na saga.
Conclusão
Conversamos sobre maneiras de organizar uma saga usando coreografia e orquestração, bem como sobre os problemas que esse padrão acarreta. A seguir, falaremos sobre maneiras de consertar algumas anomalias e enviar mensagens transacional ao corretor de mensagens.
