Tolerância a Falha
Uma característida de um sistema distribuído, que os diferencia dos sistemas simples, é a noção de falha parcial. Uma falha parcial pode acontecer, em um sistema distribuído, quando um de seus componentes falha. Este tipo de falha poderá afetar uma operação do sistema, mas não derrubar o sistema inteiro. Já nos sistemas simples, não distribuídos, uma falha geralmente derruba o sistema inteiro, ou seja afeta todos os componentes.Um dos objetivos de um sistema distribuído é a tolerância a falhas parciais, contornando-as sem que a performance do sistema seja seriamente afetada. Quando uma falha acontece, o sistema deverá manter-se em funcionamento enquanto os reparos são realizados. Ou seja, eles devem tolerar falhas, e continuar operando, mesmo na presença destas falhas, por algum tempo, até que elas sejam corrigidas.
Atomicidade é uma propriedade importante em um sistema distribuído, em uma transação múltipla, ou seja vários dados sendo atualizados em uma única transação, devem ser atualizados todos como se fossem uma única operação. Ou todas as operações são gravadas ao mesmo tempo, ou nada será gravado. Para fazer isso em um sistema distribuído deve-se utilizar um protocolo de commit distribuído.
Introdução a tolerância a falha
Conceitos básicos
Aqui serão descritos os conceitos básicos da tolerância a falha em sistemas distribuídos. Para um sistema distribuído ser tolerante a falhas, ele deverá disponibilizar os seguintes conceitos:- Disponibilidade
- Confiabilidade
- Segurança
- Manutenção
Confiabilidade é definido pelo tempo em que o sistema permanece operando, sem interrupções. Comparando confiabilidade com disponibilidade, temos que a cofiabilidade define um intervalo de tempo e a disponibilidade trata-se de um determinado instante temporal. Isto é muito relativo, mas digamos que um sistema que passa um milissegundo fora do ar a cada hora, dizemos que ele tem uma disponibilidade de 99.9999% do tempo, porém ele não é confiável. Já um sistema que nunca para, mas deve ficar fora do ar por duas semanas todo mês de Março possui uma confiabilidade bem alta, porém ele tem uma disponibilidade de 96%.
Segurança se refere a uma situação onde acontece uma falha e nada de catastrófico acontece. Por exemplo, o sistema que controla o piloto automático de um avião, ou o sistema que controla os trens de um metrô são exemplos de sistemas que possuem um alto grau de segurança. Se estes sistemas falharem temporariamente, mesmo que por alguns segundos, os efeitos poderão ser desastrosos. Estes sistemas não são fáceis de serem construídos.
Manutenção trata-se da facilidade de um sistema se recuperar em caso de falha, um sistema com fácil manutenção também possui uma alta disponibilidade, especialmente quando as falhas podem ser detectadas e corrigidas automaticamente.
Uma falha em um sistema é dada quando ele não consegue cumprir o que ele foi desenvolvido para fazer. Em um sistema com múltiplos serviços, ele irá falhar quando um ou mais serviços falham. Um erro é um estado de um sistema que pode levar a uma falha.
A causa de um erro é denominada falha, e controlar estas falhas faz parte do desenvolvimento de um sistema distribuído. Fazer um sistema que possa continuar operando mesmo com a existência de falhas.
As falhas são classificadas como transiente, intermitente ou permanente, falhas transientes são as falhas que ocorrem uma vez e nunca mais repetem, mesmo quando executamos a mesma operação. Uma falha intermitente é aquela que acontece algumas vezes, depois para de acontecer e mais para frente volta a ocorrer sem nenhuma relação aparente. Estas falhas são bem complicadas de serem diagnosticadas, uma vez que a causa dela não é conhecida. Uma falha permanente é aquela que acontece e continua acontecendo até que o componente que esteja falhando é arrumado ou substituído.
Modelos de falha
Em um sistema distribuído, muitas vezes a falha não é facilmente determinada, já que muitas vezes, os servidores destes sistemas se comunicam e em uma falha do sistema de banco de dados, pode estar sendo causada por uma máquina de armazenamento de dados e não em si do banco de dados.Para melhor entender as falhas em um sistema distribuído, foram desenvolvidas vários esquemas de classificação. Abaixo mostramos uma delas
- Falha de queda: O servidor morre, porém ele estava operando normalmente antes de morrer
- Falha de omissão: O servidor falha ao responder as requisições recebidas
- Omissão de recebimento: O servidor falha ao receber uma mensagem
- Omissão de envio: O servidor falha ao enviar mensagens
- Falha de tempo: A resposta do servidor não ocorre após um determinado período de tempo
- Falha de resposta: O servidor envia uma resposta incorreta
- Falha de valor: O valor da resposta está errado
- Falha de estado de transição: O servidor desvia do fluxo de controle correto
- Falha arbitrária: Um servidor pode produzir respostas arbitrárias a qualquer momento
A falha de omissão acontece quando o servidor falha em responder uma requisição e muitas coisas podem causar uma falha de omissão, por exemplo em uma omissão de recebimento o servidor nunca recebe a requisição. Neste caso mesmo que uma conexão tenha sido estabelecida entre o cliente e o servidor, a mensagem não chega, pois um serviço que "ouça" as requisições contidas nas mensagens, não foi estabelecido.
Um outro tipo de omissão pode acontecer quando o servidor processa a mensagem, mas falha em enviar uma mensagem de resposta. Neste caso o servidor precisa estar preparado para receber e reprocessar a mesma mensagem, cujo envio da resposta falhou.
Outros tipos de falha de omissão, podem estar relacionados a falha do software, como um loop infinito, ou uma administração errônea da memória.
A falha de resposta de tempo acontece quando a resposta chega fora de um intervalo de tempo específico. Uma resposta gerada muito cedo, pode causar um erro de memória, quando não há mais espaço para armazenar aquela mensagem. O mais comum é a resposta de uma mensagem atrasar, quando temos um problema de performance.
Uma das falhas mais graves é a falha de resposta, que ocorre quando um serviço retorna uma resposta incorreta. Existem dois tipos de falha de resposta, no caso de uma falha de valor, o serviço retorna um valor inválido, muitas vezes gerado devido a um bug na implementação do serviço, ou uma falha de estado de transição, a qual pode ser exemplificada por uma reação inesperada para uma requisição, por exemplo, quando um servidor recebe uma mensagem que ele não reconhece, uma falha de estado de transição acontece caso não haja nenhum tipo de medida realizada para administrar este tipo de mensagem.
O tipo de falha mais grave são as falhas arbitrárias, ou também chamadas de falha Bizantina, quando este tipo de falha acontece, os clientes devem estar preparados para o pior. Nestes casos, um servidor pode estar produzindo uma resposta que não havia sido planejada, e que não pode ser detectado como uma falha. Ou mesmo, um servidor pode estar trabalhando em conjunto com outros servidores, afim de produzir respostas erradas. Por isso que a segurança é um fator importante quando estamos falando de sistemas distribuídos.
Mascaramento de falha por redundância
Resiliência de Processo
Problemas de Design
Um dos principais pontos em um sistema tolerante a falha é organizar os processos em vários grupos. O ponto chave deste sistema é quando uma mensagem é enviada para um grupo, todos os membros daquele grupo devem receber aquela mensagem. Com isso caso algum elemento do grupo falhe, um outro membro poderá cuidar daquela mensagem.A proposta de introduzir grupos é fazer com que eles sejam classificados com uma abstração simples. Um processo pode ter que enviar uma mensagem para um grupo de servidores sem saber quem e quantos são estes servidores e isto pode se modificar de uma chamada a outra.
Grupos horizontais vs grupos hierárquicos
Uma diferença importante entre grupos tem haver com a organização da sua estrutura interna. Em alguns deles todos os processos são iguais, ninguém é o chefe e todas as decisões são tomadas coletivamente, estes grupos são conhecidos como:Falha de queda: O servidor morre, porém ele estava operando normalmente antes de morrer
Falha de omissão: O servidor falha ao responder as requisições recebidas
Omissão de recebimento: O servidor falha ao receber uma mensagem
Omissão de envio: O servidor falha ao enviar mensagens
Falha de tempo: A resposta do servidor não ocorre após um determinado período de tempo
Falha de resposta: O servidor envia uma resposta incorreta
Falha de valor: O valor da resposta está errado
Falha de estado de transição: O servidor desvia do fluxo de controle correto
Falha arbitrária: omo grupos horizontais. Já outros grupos possuem uma hierarquia, por exemplo um dos processos é o coordenador e todos os outros são trabalhadores. Neste caso qualquer requisição, seja ela realizada por um trabalhador interno, ou um processo externo, será direcionada ao coordenador, e ele é que decidirá qual processo será responsável por processar aquela requisição.
Cada uma destas organizações tem suas vantagens e desvantagens, os grupos horizontais não possuem um único ponto de falha, além de ele ser simétrico e no caso de um processo falhar, o grupo ficará menor, mas continuará o seu funcionamento. Porém a tomada de decisão é mais complicada, já que uma votação deverá ser realizada para a tomada de decisão.
O grupo hierárquico tem as propriedades opostas, caso o coordenador falhe, todo o grupo para de funcionar, porém enquanto o coordenador esta rodando, as decisões serão tomadas sem consultar ninguém mais.
Membro do grupo
Mascaramento de falha e replicação
Acordo em sistemas falhos
Quando o sistema está funcionando corretamente, é fácil atingir estes objetivos, porém quando um ou mais componentes falham, ai as coisas ficam complicadas.
- Sistemas síncronos versus sistemas assíncronos. Um sistema pode ser considerado síncrono quando existe um relógio controlando os processos deste sistema, e a cada passo deste relógio, todos os processos realizam uma operação. Um sistema que não é síncrono é conhecido como assíncrono
- O tempo que leva a comunicação é limitado ou não. O tempo da comunicação é limitado quando há uma garantia que todas as mensagens do sistema são entregues com um tempo máximo global.
- Entrega de mensagens é ordenada ou não. Nestes sistemas as mensagens são entregues na mesma ordem em que elas foram enviadas, ou nos casos em que não há esta garantia.
- Transmissão das mensagens é feita através de unicasting ou multicasting.
Deteção de falha
A detecção de uma falha é um ponto muito importante para podermos mascarar o acontecimento dela. Em um grupo de processos, eles devem possuir a habilidade de detectar quais processos ainda fazem parte daquele grupo e isto é realizado através da monitorização das falhas.Existem duas maneiras de se detectar a falha de um processo, na primeira delas o processo ativo envia uma mensagem de "Você ainda está vivo" para os outros processos, e através da resposta a esta mensagem é possível determinarmos quem ainda está vivo ou não. Ou ficar aguardando passivamente mensagens dos outros processos. Este segundo caso faz sentido apenas quando há muita comunicação entre os processos.
Um dos pontos cruciais de se detectar uma falha, é estabelecer um mecanismo de timeout. Existem dois grandes problemas de se detectar uma falha através de timeout, primeiro devido a problemas de comunicação entre redes, nem sempre quando uma resposta não chega, é sinal que ela não foi dada, ou seja a geração de falsos positivos é bem alta. E caso este falso positivo seja responsável por retirar um processo saudável, da lista de processos disponíveis, fica claro que existe algo de errado.
Um outro problema é que os timeouts são muito básicos, é difícil confiar em um sistema de detecção de falha que considere um processo falho, quando ele apenas deixa de responder uma mensagem.
Existem muitos fatores que devem ser levados em conta, quando estamos desenvolvendo um sistema de deteção de falha. Pode-se considerar a utilização do protocolo de fofoca, onde cada processo do sistema envia regularmente mensagens de, estou vivo e rodando, para seus vizinhos.
A utilização do protocolo de fofoca, para envio de mensagens de estou vivo irá eventualmente atingir todos os pontos do sistema, e cada um deles terá um controle de quais processos estão vivos, ou não. Um membro, o qual esta informação for muito antiga, será presumidamente considerado como falho.
Uma funcionalidade importante destes sistemas é conseguir diferenciar uma falha de comunicação, da rede, de uma falha de processamento do nó e uma as formas de resolver este problema é não deixar que apenas um processo decida se um outro processo esta falhando ou não. Neste caso ao notar que um processo não está mais respondendo aquele nó deve perguntar aos seus vizinhos se eles conseguem se comunicar com processo presumidamente falho.
Comunicação confiável entre cliente e servidor
Muitos casos de tolerância a falha em sistemas distribuídos se concentram nos processos falhos, porém também precisamos considerar as falhas de comunicação. O canal de comunicação pode apresentar falhas de queda, omissão, tempo e falhas arbitrárias. Para construir um canal de comunicação confiável o foco é omitir falhas de colisão e omissão.Comunicação ponto a ponto
A comunicação ponto a ponto confiável é estabelecida através de protocolos de transporte confiáveis, como o TCP, que omite falhas de omissão, que ocorrem por causa das mensagens perdidas, pelo uso de confirmações e retransmissões.Falhas de colisão não podem ser mascaradas. Estas falhas acontecem quando uma conexão TCP quebra abruptamente e nenhuma mensagem podem ser transmitidas através do canal. Na maioria das vezes o cliente é informado que existe uma colisão no canal através do disparo de uma exceção. A única forma de mascarar este tipo de falha é deixar o sistema tentar se reconectar automaticamente, o que pode ser feito através do reenvio de uma requisição de conexão.
Semântica RPC na presença de falhas
Nesta seção vamos analisar a comunicação cliente-servidor quando elas utilizam um tipo de comunicação como o RPC. O objetivo de uma RPC é esconder a operação de comunicação fazendo com que as chamadas RPC sejam realizadas como se fossem chamadas locais. E isso funciona muito bem enquanto o cliente e o servidor funcionem corretamente. O problema acontece quando os erros acontecem, nestes casos fica difícil mascarar os problemas como se eles fossem chamadas locais.Vamos dividir as falhas RPCs em 5 diferentes classes de erros
- O cliente não consegue localizar o servidor
- A mensagem é perdida entre o cliente e o servidor
- O servidor quebra após receber a requisição
- A mensagem de resposta do servidor ao cliente é perdida
- O cliente quebra após enviar a requisição
O cliente não consegue localizar o servidor
Este erro pode acontecer quando, por exemplo, todos os servidores estão desligados, quando o cliente está esperando uma certa versão dos serviços que já não está mais disponível. Neste caso ao tentar se conectar com este servidor uma falha acontecerá. Este tipo de erro é usado para proteger o cliente de tentar usar uma versão errada do serviço, que contenha parâmetros diferentes dos quais o cliente está preparado para lidar. O grande problema é como atuar quando este tipo de erro acontece.Uma solução possível é fazer com que este erro dispare uma exceção ou sinais. Esta solução não pode ser utilizada em todas as linguagens, já que em algumas delas não é possível disparar uma exceção ou um sinal. Um outro problema é que ter que escrever exceções para este tipo de erro vai contra o objetivo de ser transparente quanto a execução remota deste tipo de chamada.
Mensagem de requisição perdida
Dos tipos de erro de RPC este é o mais fácil de ser tratado, basta iniciar um contador, assim que a mensagem é enviada. Se o tempo do contador ultrapassar um limite estabelecido antes que uma resposta chegue, a mensagem será reenviada. Caso a mensagem tenha sido realmente perdida o servidor não vai conseguir identificar nenhuma diferença entre a mensagem original e a cópia e tudo funcionará corretamente. A não ser que muitas mensagens sejam perdidas e o cliente desiste achando que o servidor quebrou, o que nos leva a um erro de servidor não encontrado.Servidor quebrou
A sequência normal de eventos é a mensagem chega, é processada e uma resposta é enviada. Existem dois caminhos possíveis de falha quando o servidor quebra, na primeira delas, a mensagem é recebida o servidor processa ela e logo após ele quebra, não enviando nenhuma resposta. Na segunda, o servidor recebe a mensagem e quebra, antes mesmo de processar a mensagem.A parte mais complicada de se tratar este tipo de erro é que atitudes diferentes devem ser tomadas para solucionar estes dois tipos de problema. No primeiro caso o sistema deve enviar um relatório de erro de volta ao cliente, já no segundo uma simples retransmissão resolve o problema.
Existem três tipos de filosofias para estes problemas, na primeira os processos ficam esperando o servidor ser reinicializado e tentam realizar a operação novamente. A mensagem será retransmitida até que o servidor responda. Esta técnica é chamada de pelo menos uma vez, já que neste caso há uma garantia que o RPC foi executado pelo menos uma vez, porém possivelmente mais de uma.
No segundo tipo de filosofia, ele desiste de executar a requisição e emite uma mensagem de erro. Esta solução é chamada de no máximo uma vez, já que só uma requisição será enviada.
A terceira filosofia não garante nada, quando um servidor quebra, os clientes não recebem nenhuma ajuda ou pista sobre o que aconteceu. A chamada de RPC pode ter sido processada nenhuma ou muitas vezes.
Nenhuma destas três filosofias é muito atrativa. Imagine uma operação remota para imprimir algum texto, e que o servidor de impressão envie uma mensagem de confirmação para o cliente, quando o texto é impresso. Neste caso, quando o servidor recebe uma instrução de impressão ele envia uma mensagem de confirmação de recebimento da instrução para o cliente. Existem duas estratégias que o servidor pode seguir, ele pode enviar uma mensagem de que o texto foi impresso antes de mandar a instrução de impressão para a impressora, ou após o texto ter sido impresso.
Imagine que o servidor pare de funcionar e logo depois se recupere. Ele envia uma mensagem para os clientes dizendo que o servidor caiu e voltou. O grande problema é que os clientes não sabem se a sua requisição de impressão foi recebida ou não.
O cliente pode seguir quatro estratégias. Primeiro o cliente pode decidir não reenviar a requisição, assumindo o risco do texto nunca ser impresso. Segundo o cliente pode decidir sempre reenviar a requisição de impressão, correndo o risco de o texto ser impresso duas vezes. Terceira ele pode decidir reenviar a requisição somente se ele não receber a mensagem de confirmação de recebimento da instrução de impressão pelo servidor, neste caso o cliente estão levando em conta que o servidor caiu antes que a mensagem da instrução de impressão tenha sido recebida. A quarta estratégia é reenviar a requisição somente quando ele receba uma mensagem de confirmação de recebimento da mensagem de solicitação de impressão pelo servidor.
Contando com duas estratégias para os servidores e quatro para os clientes, temos oito possíveis caminhos, mas nenhum deles será satisfatório.
Resumindo a possibilidade do servidor cair, muda radicalmente a natureza de uma chamada RPC e claramente a distingue de um sistema simples.
Perda de mensagens de resposta
Queda do cliente
Apresentação