Microsserviço 6
Podemos utilizar vários microsserviços colaborando entre si para implementar um processo de negócio...
Pensando no contexto de transações de banco de dados, temos várias ações que precisam ocorrer, mas queremos tratar todas como uma unidade. Utilizamos transações para garantir que uma ou mais mudanças de estado sejam realizadas com sucesso.
Para que essa garantia aconteça, o banco de dados fornece o modelo ACID:
- atomicidade: ou todas as operações acontecem, ou todas falham;
- consistência: as alterações mantêm o banco de dados em um estado válido;
- isolamento: várias transações podem ser executadas simultaneamente sem interferirem entre si; e
- durabilidade: após a conclusão de uma transação, os dados não serão perdidos caso haja uma falha no sistema.
Considerando o princípio de atomicidade, ao decompor uma operação única em duas transações separadas, perdemos a garantia de atomicidade. Essa falta de atomicidade pode causar problemas significativos, especialmente ao migrar sistemas que dependem dessa propriedade.
Um algoritmo comum para implementar transação distribuída é o two-phase commit.
Two-Phase Commit
O two-phase commit é usado para permitir mudanças transacionais em sistemas distribuídos, nos quais vários processos participam da operação. Esse algoritmo é dividido em duas fases:
- fase de votação: um coordenador consulta os workers participantes para confirmar se a mudança de estado pode ser feita. Se todos confirmarem, o algoritmo passa para a próxima fase (commit). Se algum worker negar, a operação é cancelada; e
- fase de commit: o coordenador envia a requisição de commit para todos os workers. Não é possível garantir que todos os commits ocorram no mesmo instante, pois as mensagens podem chegar em momentos diferentes para cada worker. Quanto mais workers, maior a latência do sistema e maior o período em que os recursos estarão travados (distributed lock).
Assim, evite essa abordagem sempre que possível, mantendo os estados em um único banco de dados e centralizando a gestão de estados em um único serviço.
Sagas
Mesmo com sagas, não teremos a atomicidade fornecida pelo ACID. A atomicidade existirá apenas dentro de cada transação individual dos serviços que compõem a saga.
Com sagas, é possível executar operações que envolvem vários serviços, coordenando várias mudanças de estado sem o uso de locking, permitindo uma execução independente.
O primeiro ponto de apoio das sagas é a necessidade de modelar explicitamente o processo de negócio. Dividimos o processo em um conjunto de chamadas que serão feitas para diferentes serviços colaborativos.
Como a saga é dividida em transações individuais executadas em serviços distintos, devemos considerar a recuperação em caso de falha. Existem duas abordagens:
- recuperação com retrocesso (backward recovery): rollback de transações que já tenham feito commit; e
- recuperação com avanço (forward recovery): a partir de checkpoints, continuar do ponto onde ocorreu a falha. Para isso, precisamos de informações suficientes para que novas tentativas sejam realizadas.
O objetivo é recuperar falhas de negócio, e não falhas técnicas. Falhas técnicas, como um erro 500, devem ser tratadas separadamente.
Como são várias transações envolvidas em uma saga, para fazer rollback de uma transação que já teve commit, precisamos de uma transação compensatória, um rollback semântico. Por exemplo, um email enviado ao cliente informando que o pedido está a caminho. Não há como "desenviar" o email. A transação compensatória seria enviar outro email ao cliente informando, por exemplo, que houve um problema no pedido e que este foi cancelado.
Antes de chegar ao ponto de usar uma transação compensatória, vale a pena reorganizar os passos do workflow original. Às vezes, é possível simplificar o rollback ajustando o fluxo do workflow.
Implementando Sagas
Existem dois estilos de implementação de sagas:
- sagas orquestradas: usa um coordenador central que controla o que acontece e quando acontece. Esse coordenador chama os serviços para executar um workflow usando request/response. A vantagem é a facilidade de compreensão, pois a análise ocorre em um único ponto. A desvantagem é o maior acoplamento, pois o coordenador precisa conhecer todos os serviços envolvidos. Além disso, o coordenador pode acabar absorvendo a lógica de negócio que deveria estar nos serviços; e
- sagas coreografadas: abordagem menos acoplada, distribui a responsabilidade do workflow entre os serviços que colaboram entre si. Geralmente, faz uso intensivo de eventos entre os serviços. Eventos são gerados, e os microsserviços reagem aos eventos recebidos. Não enviamos eventos diretamente aos microsserviços, apenas os geramos, e os microsserviços interessados reagem. Vários microsserviços podem reagir a eventos se usarmos um tópico.
Independentemente da abordagem, o uso de um ID de correlação é essencial para rastrear o workflow e saber o estado atual, especialmente em sagas coreografadas.
Os modelos, two-phase commit e sagas, não são mutuamente exclusivos e podem ser combinados para melhor atender ao processo de negócio.
Resumindo:
- sagas orquestradas: request/response;
- sagas coreografadas: event-driven.