Tolerância a falhas

Criando aplicações tolerantes a falhas

Esse termo é muito usado atualmente, principalmente se você está habituado a trabalhar com microsserviços, ou qualquer arquitetura semelhante.

Esse conceito de ter aplicações resilientes, que conseguem se manter ativas após erros "criticos", já vem sido trabalho e aprimorado a algumas décadas.

Basicamente usamos esse termo "tolerância a falhas" para aplicações com a capacidade continuar provendo seus recursos, mesmo após falhas de software ou hardware.

Se existe uma certeza em microsserviços é de que eles falharão com uma certa frequência e mantê-los preparados para acidentes é seu objetivo como programador.

Aqui segue algumas dicas para criar aplicações bonitas e que se recuperem de falhas.

Problemas operacionais

Problemas operacionais não são causados diretamente por um problema técnico e por esse motivo muitas vezes são desconsideradas, segue dois exemplos de falhas que eu considero "operacionais":

Um grande exemplo é no caso de empresas que não possuem um processo de deploy automatizado e isso é feito de forma manual ou parte do processo manual, por trabalhar dessa forma além de perda de tempo você abre as portas para muitas falhas, como: esqueci de mover o arquivo x para a pasta public no servidor e por isso estamos fora do ar, ter um ambiente com deploy automatizado utilizando git, por exemplo, não é mais opção, mais, sim, obrigação.

Outro problema operacional é revisão incompleta de código. Esse problema é frequente ao ter um ambiente com alta velocidade de desenvolvimento, e acontece quando a garantia do seu código está na mão de outro desenvolvedor, que pode ou não estar 100% habituado com seu projeto, dessa forma é muito comum durante longas revisões muitos problemas passarem despercebidos. Para lidar com esse problema você pode iniciar escrevendo testes com uma alta cobertura, envolvendo vários senários possíveis, isso diminui muito o problema de falhas operacionais.

Evitar pontos unicos de falhas

O termo usado para esse problema é SPOF (Single Point of Failure). Para evitar esse problema precisamos entrar na arquitetura de nosso sistema, se existe algo que possa derrubar todo o microsserviço, aí está um potencial problema.

Por exemplo, se o meu banco de dados não suporta um determinado número de conexões e essa quantidade máxima for superada, possivelmente sua aplicação ficará fora do ar, uma forma de resolver esse problema é tendo outras instâncias/replicas do banco para nesses casos sua aplicação não ficar na mão.

Mesmo não tendo implementado todos os serviços de redundância e garantias, é muito importante ter todos os potencias problemas previamente identificados.

Testes de carga e de código são muito importantes, para evitar que identifiquem esses problemas em produção.

Recuperação de falhas

Aplicações que se recuperam de falhas são elegantes.

Sabemos que falhas ocorrerão, e que já mapeamos várias delas, mais quando algo inesperado acontecer é muito importante saber como lidar com isso, podendo ser implementado de várias formas dependendo da tecnologia que você utilize.

Algumas plataformas oferecem a opção de sempre que algo catastrófico acontecer, reiniciar sua aplicação.

Ao resolver esse problema você terá um uptime bem alto, algo na casa dos 99,9%

Para ilustrar, segue um exemplo de implementação usando nodejs:

No código abaixo configurei o node para funcionar com vários processos, nesse caso será executado um processo para cada núcleo.

import cluster from 'cluster'
import os from 'os'

...

if (cluster.isPrimary === true) { 
    os.cpus.forEach(() => cluster.fork())
} else {
    app.listen(port, () => { ... });
}

Em seguida, configuramos para sempre que um processo morrer, automaticamente um novo ser executado no lugar.

cluster.on('exit', (worker, code) => {
    if (code !== 0) cluster.fork();
})

Essa é uma implementação simples, porém pode ser feita de forma mais elaborada.

Provavelmente quando estiver resolvendo isso, por tabela você vai precisar lidar com monitoramento, sendo algo muito interessante.

Caso queira entender melhor sobre recuperação de falhas, vale a pena conhecer o Erlang e sua maquina virtual que implementa a recuperação de falhas nativamente, ela vai lhe trazer várias ideias.

Conteudo também disponível no meu blog: https://www.jeantux.com/post/tolerancia-a-falhas

Até mais pessoal! 😎

Caramba @jeantux, achei MUITO interessante o que você postou. Pouca gente fala sobre isso com pessoas que estão começando e falta um pouco de visão pra galera no início.

Uma dica sobre SEO, vi que publicou este mesmo texto no seu blog. O Google não gosta de conteúdo duplicado e se você compartilha o mesmo texto aqui acaba prejudicando o seu próprio conteúdo, pois o tabnews com o tempo tende a ganhar mais força no Google.

Se eu fosse você na próxima daria uma reescrita no texto para algo mais exclusivo aqui pro tabnews. E mesmo assim colocaria a fonte como seu blog.

No mais, valeu demais pelo conteúdo

Dica incrível @bgabraga! Muito obrigado!

Uma das langs que eu conheço que tem tolerância a falhas é elixir, uma linguagem brasileira que abstrai a maquina virtual do erlang(beam) e sobre threads abstraida em processos.

Da forma que o elixir faz, você pode ter erro critico em um processo, você pode matar ele e subir um novo processo que continuaria o que o anterior falhou