Padrões de Consistência e Disponibilidade
Tags: systemdesign, learning, microservices, interview
Published: 2023-04-19 14:52:56

No ultimo post acabei dando uma pincelada em CAP theorem e sobre o que ele significa, agora nessa part two vou tentar entrar um pouquinho nos detalhes de modelos / padrões de consistência e disponibilidade.

Só dando um recap, temos como verdade que basicamente do C A ou P faremos a escolha em C e A, Partition tolerance, ou, habilidade do sistema de continuar funcionando mesmo que haja erro de comunicações entre seus componentes, vai ter de ser sempre tolerado, uma vez que estamos falando de um sistema distribuído que se comunica através da rede.

# Padrões de consistência

Quando focamos em CP, normalmente estamos buscando uma consistência forte, ou seja, seu sistema como um todo ou retorna uma resposta consistente (o read exato do ultimo write) ou retorna um erro.

Essa escolha normalmente é feita quando seu business precisa de R/W atômicas (leitura e escrita).

## Quais os desafios?

Com múltiplas copias do mesmo dado (nodes) ou com seus dados estando distribuídos em diferentes lugares, temos o dilema de como sincronizar esses nós para que os clientes recebam uma visão consistente desses dados, podemos lidar com esse problema de algumas formas:

## Eventual consistency

Após cada escrita, as leituras podem ser ou não a mais atualizada, sem garantia de que verá essa ultima versão. O sistema fará o máximo para que consiga sempre mostrar o dado mais atualizado.

Eventual consistency é aplicado quando NÃO queremos uma consistência forte.

Normalmente a técnica empregada em sistemas distribuídos é a de replicar o dado em processos separados do de escrita (processo de processador mesmo, ou outra thread, o conceito é mais fazer async).

## Strong consistency

Após cada escrita, novas leituras vão obrigatoriamente ver o dado mais recente.

Para alcançar consistência forte, inevitavelmente iremos comprometer escalabilidade e performance, porém se for um requerimento indispensável para o sistema devemos lidar com ele.

Normalmente a técnica empregada pra aplicar consistência forte é a de dar um lock no dado enquanto ele é modificado, ou seja, se alguém solicitar a leitura vai precisar esperar até que a operação de escrita/replicação esteja concluída.

Exemplos de aplicações que necessitam de consistência forte são bancos, sistemas de passagem / ingressos, ou qualquer contexto onde existe restrições de numero de uso / recursos.

Em termos de implementação temos alguns padrões que surgem quando necessitamos lidar com esse tipo de situação em um mundo de serviços distribuídos, os que me veem a cabeça agora são:

- SAGA e 2PC (2 Phase commit) para transactions.
- Distributed lock para operações que precisam de lock.

Não vou entrar nos detalhes agora, fica pra um próximo post, mas ficam os nomes ai caso alguém não conheça.

# Padrões de Disponibilidade

Ao focar em AP o nosso sistema retorna a resposta mais consistente que está imediatamente disponível, ou seja, após a escrita o sistema pode retornar um dado “antigo” e saberemos lidar com essa situação. Escritas podem demorar algum tempo até que se propaguem por todo o sistema.

AP é a escolha quando o negócio permite eventual consistency ou quando ele precisa continuar funcionando independente de erros.

Normalmente se você esta lidando com sistemas cloud-based (o padrão de hoje em dia) e projetados para milhões e milhões de users (nem sempre é o caso, mas se você esta lendo sobre system design provavelmente é o caso, pelo menos da entrevista que você vai precisar passar), então bem vindo, estamos no cluster certo, normalmente você vai escolher disponibilidade com o máximo de consistência possível, delegando somente o menor pedaço possível para a consistência forte, o foco vai ser QUASE SEMPRE em escalabilidade e disponibilidade (FAZ A COMPRA ACONTECER, DEPOIS NOIS PROCESSA, ENVIA, WHATEVER, SALVA A **** DA COMPRA, gritou um PM pra algum dev cansado de e-commerce).

OBS: achou que o post estava sério demais até agora né hahaha, se não falar alguma merda não tem meu carimbo.

## Disponibilidade em números

Disponibilidade é frequentemente mensurada em números de 9.

Esse calculo se da dividindo o tempo que o sistema ficou indisponível pelo tempo total em um determinado período, por exemplo:

Um serviço que ficou 8h 45min e 57s fora do ar em um espaço de 1 ano é dito ter a disponibilidade de 3 9s, ou seja, 99,9%.

Então toda vez que estivermos falando em números de 9s em disponibilidade estaremos falando de quanto tempo o sistema fica indisponível naquele período (que normalmente é extrapolado para um ano).

Agora sobre os padrões de fato, normalmente temos dois mais comuns:

## Fail-over

Normalmente é um padrão mais utilizado para gestão de servers / serviços. Esse gerenciamento vai ser feito através de um kubernetes da vida que vai aplicar esse e diversos outros padrões pra gestão dos seus containers.

No fail-over é traçado algumas estratégias para substituição ou redirecionamento de requests em caso de falha, temos 2 estratégias principais

### Active-passive

Aqui temos uma máquina ativa respondendo as requisições e uma passiva que fica recebendo “heartbeats” (nada mais é que um ping entre os servers), assim que os heartbeats sessarem, o server passivo acaba entrando em ação e pegando o IP do ativo, assumindo seu lugar e respondendo as requisições.

O tempo de indisponibilidade é determinado pela questão do server passivo estar rodando em um estado “hot” (preparado pra receber requisições, logo consumindo mais recursos) ou “cold” (num standby mais profundo, onde vai ser preciso iniciar alguns processos para que ele fique pronto pra responder requisições).

Importante ressaltar aqui que somente o server ATIVO responde requisições nesse modelo.

### Active-active

Neste modelo duas ou mais réplicas de uma mesmo server respondem as requisições, que são distribuídas através de um load balancer entre elas, ou seja, todas tem o mesmo poder de responder requisições.

Idealmente não existe indisponibilidade a não ser que todas as replicas falhem ao mesmo tempo, porém pode existir lentidão, quanto mais replicas falhem, uma vez que o tráfego vai ficar concentrado nas que restam de pé.

Aqui vale ressaltar novamente que com a ajuda do Kubernetes você vai, normalmente, aplicar essas e diversas outras técnicas pra orquestrar seus servers e deixar o máximo de availability possível.

## Replication

Normalmente quando falamos de replication estamos falando de dados, esses dados podem estar distribuídos de diversas formas, em diversas arquiteturas diferentes, pode ser replicação em regiões diferentes (regiões geográficas), clusters de banco, nós dentro de um mesmo cluster, e por ai vai.

Por ser um assunto bem extenso por si só não vou me aprofundar aqui, entraremos nesse mérito mais pra frente em post específicos abordando DNS, CDN, Load Balancer e bancos de dados específicos.

Com esse post encerramos de CAP e suas nuances, dá pra aplicar pro Google já? Claro que não hahaha, mas já é o começo :)
Bons estudos.