next up previous

Tolerância a falhas no projeto DPC++

Elgio Schlemer - Marcos Ennes Barreto - Rafael Bohrer Ávila
Porto Alegre, julho de 1998

Introdução

DPC++ é um projeto do grupo ProcPar voltado ao desenvolvimento de aplicações distribuídas. DPC++ consiste em uma linguagem de programação, baseada em C++, que permite ao programador aliar os benefícios do paradigma de orientação a objetos com o ganho em performance decorrente da execução distribuída de sua aplicação.

Por atuar diretamente sobre ambientes de natureza distribuída -- tipicamente uma rede local de estações de trabalho -- as aplicações DPC++ tornam-se bastante sucetíveis à ocorrência de falhas, devido, principalmente, ao uso intenso do meio de comunicação. Este fato motivou, no projeto, o surgimento do estudo de técnicas de Tolerância a Falhas.

Em face de algumas particularidades oriundas do modelo funcional de DPC++, as técnicas tradicionais de Tolerância a Falhas, que, de forma geral, não levam em conta as particularidades das aplicações, podem ser remodeladas de maneira a tirar benefícios dessas particularidades, tornando o estudo de Tolerância a Falhas em DPC++ especialmente interessante.

Assim sendo, o presente trabalho visa apresentar como os mecanismos tradicionais de Tolerância a Falhas vêm sendo aplicados em DPC++ e quais as perspectivas que se têm em função das novas tendências do projeto.

Uma visão geral de DPC++

O projeto DPC++ [CAV93, ] iniciou-se em 1993, no grupo ProcPar. Em linhas gerais, a finalidade do projeto é a obtenção de uma ferramenta para o desenvolvimento de aplicações distribuídas. Desta forma, DPC++ resultou em uma linguagem de programação que, através de um pequeno conjunto de extensões a C++, oferece ao programador a capacidade de gerar aplicações distribuídas de maneira natural, por meio do uso do paradigma de orientação a objetos, obtendo, no final, os benefícios do ganho em performance oriundo do paralelismo.

Utilizando o paradigma de orientação a objetos, DPC++ baseia-se no conceito de objetos distribuídos. Estes nada mais são que objetos convencionais, com todas as características já conhecidas, porém com a capacidade de serem instanciados em outro nó do sistema. Isto faz com que haja execução concorrente entre dois objetos em nós distintos, ocasionando o ganho em performance.

A figura 1 mostra uma visão geral de uma aplicação DPC++gif, com os objetos trocando mensagens exatamente como ocorre nas aplicações orientadas a objetos que se conhece. A única diferença é que, neste caso, as mensagens são, na prática, trocadas através do meio de comunicação, e não diretamente pela memória RAM.

Figura 1: Visão geral de uma aplicação DPC++.

Como sugere a figura, cada objeto distribuído é, na prática, um processo.

Embora haja o paralelismo de execução entre objetos distribuídos, internamente um objeto é seqüencial, de forma que ele se comunica com apenas um outro objeto de cada vez. Essas duas características mantém o mesmo comportamento de um programa C++ convencional, facilitando a tarefa do programador.

Um elemento extra está, ainda, presente nas aplicações DPC++. Esse elemento, chamado de Diretório, é também um objeto distribuído, mas com tarefas especiais. É ele o responsável pela criação dos objetos distribuídos, além de manter um conjunto de informações sobre cada objeto (por exemplo, em que nó ele está instanciado) e sobre o sistema.

Ao executar um comando new, por exemplo, para criar um novo objeto distribuído, um objeto envia, na verdade, uma mensagem ao objeto Diretório solicitando a criação. Este, por sua vez, escolhe um nodo apropriado (de acordo com técnicas de balanceamento de carga) e instancia o objeto. Este ``endereço'' é, então, retornado ao objeto original, que agora pode comunicar-se com o novo objeto. Cabe lembrar que este procedimento é totalmente transparente ao programador da aplicação.

A figura 2 ilustra uma aplicação DPC++ durante o processo de criação de um novo objeto distribuído.
 

Figura 2: Criação de um objeto distribuído.
No mais, todas as características de aplicações orientadas a objetos, como herança, métodos construtores e destrutores, etc., são mantidas nas aplicações DPC++, de forma a tornar o desenvolvimento de aplicações distribuídas uma tarefa de fácil assimilação.

Checkpoints distribuídos em DPC++

O modelo de suporte a tolerância a falhas atualmente empregado em DPC++ faz uso de um mecanismo para criação de checkpoints distribuídos [SAN96, PIL97, PIL97a], ou pontos de recuperação distribuídos. Este mecanismo baseia-se em características peculiares das aplicações DPC++ para estabelecer um protocolo que rege a criação de checkpoints de modo a sempre manter um estado global consistente entre os objetos distribuídos.

Criação de checkpoints distribuídos

O método utilizado define um algoritmo que garante que a criação de um ponto de recuperação nunca possa ocasionar a perda de mensagens, mantendo assim a consistência do sistema. Como o comportamento das aplicações DPC++ é bem definido e a troca de mensagens é restrita (ou seja, ela só ocorre em situações específicas, quando da invocação de métodos ou na resposta a estes), a consistência é garantida criando-se, a cada mensagem enviada, um ponto de recuperação no objeto emissor e no objeto receptor. Após a criação dos checkpoints, os objetos trocam mensagens de confirmação (ACK) para indicar que os pontos foram corretamente criados.

A figura 3 ilustra a criação de pontos de recuperação quando da troca bem sucedida de uma mensagem entre dois objetos distribuídos, bem como as mensagens de confirmação definidas pelo protocolo estabelecido:

Figura 3: Criação de checkpoints distribuídos

O objeto tex2html_wrap_inline279 envia uma mensagem ao objeto tex2html_wrap_inline281 e cria seu ponto de recuperação. Ao receber a mensagem, tex2html_wrap_inline281 cria o seu checkpoint e envia uma mensagem de confirmação. Ao receber esta última, tex2html_wrap_inline279 envia nova confirmação, determinando o estabelecimento de um estado consistente entre os dois objetos, visto que a mensagem original foi bem recebida e os pontos de recuperação corretamente criados.

O protocolo que rege esse mecanismo garante que, na ocorrência de uma situação de falha em um dos objetos, seja possível restaurar o sistema a um estado global consistente. Esta característica advém de uma série de particularidades das aplicações DPC++.

Por exemplo, a figura 4 mostra uma situação de falha no recebimento de uma mensagem por parte de tex2html_wrap_inline281:

Figura 4: Situação de falha na comunicação entre dois objetos
Se tex2html_wrap_inline281 houvesse falhado após ter recebido a mensagem e criado o seu checkpoint, a informação sobre o mesmo, dada pelo Diretório, indicaria a tex2html_wrap_inline279 que o checkpoint de tex2html_wrap_inline281 foi corretamente criado e, portanto, a mensagem foi bem recebida. Neste caso, não existe a necessidade de envio de mensagens de confirmação, e os dois objetos podem seguir seu processamento. A mesma análise pode ser feita para o caso de falha em tex2html_wrap_inline279.

Duas características particulares da comunicação entre objetos em DPC++ justificam a eficácia do mecanismo de tolerância a falhas utilizado:

Desta forma, o fato de se garantir a consistência entre os dois objetos envolvidos em uma comunicação garante o estado global consistente do sistema.

Uma outra situação de falha que se poderia imaginar seria um objeto (como tex2html_wrap_inline281, por exemplo) receber mais de uma mensagem ao mesmo tempo. Novamente, as características de DPC++ garantem o funcionamento do mecanismo. Pelo fato de os objetos em DPC++ serem seqüenciais, ou seja, não existe paralelismo internamente a um objeto, as requisições de execução de métodos são tratadas em ordem FIFO, de modo que a segunda requisição será armazenada e só será tratada depois que todo o processo de recebimento e efetivação da primeira tiver sido completado.

Análise do mecanismo apresentado

Fazendo-se uma comparação do algoritmo aqui apresentado com o algoritmo de Koo e Toueg, apresentado por Jalote [JAL94], nota-se uma diferença nas abordagens adotadas. O algoritmo de Koo e Toueg envolve, potencialmente, um número maior de processos, quando do estabelecimento de um ponto de recuperação global, o que acarreta em maior troca de mensagens e mais tempo para a criação dos checkpoints. Por outro lado, esta operação pode ser feita a qualquer momento, e portanto o instante exato de sua execução pode ser decidido levando em conta, por exemplo, a carga momentânea do sistema, ou o tempo total de execução do algoritmo paralelo, de forma a minimizar a interferência do mecanismo de tolerância a falhas no desempenho final da aplicação.

O mecanismo de tolerância a falhas de DPC++, por sua vez, requer uma quantidade menor de mensagens quando da criação de um checkpoint, e portanto esta operação pode, isoladamente, ser executada mais eficientemente do que aquela introduzida por Koo e Toueg. Porém, a influência global do mecanismo de checkpoints distribuídos apresentado depende do grau de comunicação entre os objetos distribuídos, sendo diretamente influenciado pela estrutura da aplicação. Como os pontos de recuperação são criados a cada troca de mensagens, quanto mais freqüentemente os objetos se comunicarem, maior será a influência do mecanismo de tolerância a falhas no desempenho final da aplicação.

Tendo em vista a natureza distribuída de DPC++, onde o ``grão'' de paralelismo é maior, pode-se considerar o mecanismo proposto como adequado, pois a estrutura de aplicações DPC++ tende a apresentar um padrão de comunicação relativamente baixo. Como a comunicação entre nodos distintos tem um alto custo em relação ao processamento, é normal que um objeto distribuído comunique-se com pouca freqüência, realizando alguma tarefa computacional durante a maior parte de sua vida. Na prática, a utilização do mecanismo descrito conseguiu efetivamente introduzir um nível de tolerância a falhas nos objetos distribuídos de DPC++, conseqüentemente aumentando sua confiabilidade [PIL97].

Novas tendências de tolerância a falhas em DPC++

Modelo de replicação do Diretório

O novo modelo para o objeto Diretório tem o objetivo principal de garantir a tolerância a falhas neste objeto através da sua replicação. Desta forma, uma falha de crash no nodo onde este objeto está executando não mais fará com que todo o sistema pare e não possa mais executar.

A função do objeto Diretório pode ser dividida em três partes distintas, conforme mostra a figura 5. O autômato finito definido na figura descreve a primeira função do objeto Diretório como sendo a criação do seu objeto backup. Na primeira vez que o Diretório é criado, o ambiente DPC++ deverá também criar o mesmo objeto Diretório em outro nodo da rede, que funcionará como um objeto Diretório backup.

Figura 5: Representação do modelo proposto para o objeto Diretório.

A segunda parte é caracterizada pelo fato do Diretório primário estar funcionando perfeitamente, por isso este estado é considerado como estado final do autômato finito. A função descrita na terceira parte é a restauração do Diretório primário. No momento da execução, a situação de falha é assumida pelo objeto distribuído quando este faz uma requisição ao Diretório primário e não obtém resposta dentro de um tempo limite. A próxima ação a ser realizada pelo objeto distribuído é fazer a mesma requisição ao Diretório backup, que, concluindo que o primário falhou, assume a função de primário e cria um novo Diretório backup em outro nodo da rede.

Uma vez assumindo como Diretório primário, uma mensagem é enviada para todos os objetos distribuídos contendo os novos identificadores do Diretório primário e do Diretório backup. O modelo final de replicação do Diretório a ser utilizado no DPC++ é ilustrado na figura 6.

Figura 6: Modelo de replicação do objeto Diretório no DPC++

No modelo apresentado na figura 6, podem ser distinguidos três momentos nos quais uma falha pode ser detectada. Estes momentos estão referenciados como tex2html_wrap_inline319tex2html_wrap_inline321tex2html_wrap_inline323.

A falha definida em tex2html_wrap_inline319 ocorre antes do Diretório primário enviar a mensagem de atualização ao Diretório backup. A falha tex2html_wrap_inline321 aparece depois que (ou enquanto) a mensagem de atualização foi enviada e antes que o Diretório primário receba uma confirmação. A falha expressa em tex2html_wrap_inline323 ocorre após o objeto distribuído receber a resposta da requisição feita por ele ao Diretório primário.

Nos dois primeiros casos, o objeto distribuído não receberá a resposta de sua requisição e detectará a falha do Diretório primário. Em conseqüência, ele se comunicará com o Diretório backup, iniciando, assim, o processo de recuperação do Diretório primário. No terceiro caso, a falha é transparente ao objeto distribuído, já que este recebeu a resposta de sua requisição. Esta falha somente será detectada quando algum objeto distribuído tiver que se comunicar com o Diretório primário.

Implicações do novo nível operacional de DPC++

Acompanhando novas técnicas, e para possibilitar a exploração de novos recursos de comunicação, o projeto DPC++ está sendo reestruturado. O que se tinha até aqui era uma linguagem com muitas restrições e nenhuma exploração do paralelismo de grão fino, utilizando sockets para comunicação entre objetos. Esta nova reestruturação deve suportar paralelismo entre métodos e oferecer suporte a várias bibliotecas de comunicação (como PVM e MPI), além do mecanismo usado atualmente (sockets sobre protocolo UDP). A definição concreta deste modelo ainda é fonte de estudos, de modo que serão mostradas algumas idéias e, principalmente, como elas afetam o modelo de tolerância a falhas com checkpoints distribuídos.

Concorrência entre métodos

Uma das grandes limitações do modelo atual do DPC++ é que cada objeto, instanciado como um processo em uma máquina, pode atender apenas uma requisição de método de cada vez, e este objeto não atende nenhuma outra requisição enquanto estiver ocupado atendendo a primeira. Neste caso, se houver um objeto requisitando um método a outro objeto que já está atendendo uma requisição, este deverá esperar o término do método até ser atendido.

Uma das maneiras de se implementar concorrência entre métodos seria fazer com que cada requisição fosse atendida em um novo thread. Existiria, então, um thread responsável pelo atendimento das requisições, o qual criaria um novo thread para atender cada requisição, liberando-se imediatamente para poder atender outras.

Isto, por si só, já inviabiliza o atual modelo de checkpoints de DPC++, pois ele baseia-se no fato de que cada objeto atende apenas um método de cada vez e, portanto, só haverá troca de mensagens entre o objeto que requisitou o serviço e o objeto que o está atendendo, até que a transação seja concluida, onde então, o objeto que atendeu voltará a estar disponivel e o que requisitou continuará sua execução. Pode-se garantir que, se um objeto falhar, apenas o objeto que o estava requisitando será afetado, ou apenas o que o estava atendendo. Os outros não serão comprometidos, mesmo que venham a requisitar métodos a um objeto que falhou. Neste caso, como visto, este objeto pedirá o seu status ao Diretório, e este lhe retornará o endereço do novo objeto.

Quando se possibilita atender várias requisições ao mesmo tempo, não se tem mais esta propriedade, pois existiriam objetos trocando mensagens com vários outros, através de seus threads, e se este falhar, pode-se ter vários objetos afetados.

Apenas um processo por nodo

Outra idéia que surgiu é a utilização de apenas um processo por nodo, isto é, apenas um processo por máquina. Este processo, por sua vez, seria capaz de criar qualquer objeto que seja requisitado, e estes objetos poderiam atender várias requisições. A utilização de apenas um processo foi proposta, principalmente, para diminuir o tempo de latência na criação de objetos, já que, antes, eles eram criados como processos, e agora como threads. Neste caso, uma falha de crash (tipo de falha que o atual modelo suporta) comprometeria todos os objetos existentes naquele nodo e todos os demais que se comunicam com eles, pois todos pertencem ao mesmo processo agora.

Com as modificações propostas para o DPC++, o atual modelo de tolerância a falhas através de checkpoints deve ser cuidadosamente revisto. Adaptá-lo para suportar estas alterações talvez seja inviável, sendo possivelmente melhor partir para estudos de outras técnicas de TF.

Considerações finais

Conforme previsto, este trabalho apresentou uma visão das atividades relativas a tolerância a falhas atualmente conduzidas no projeto DPC++. O uso de técnicas já conhecidas de tolerância a falhas é adaptado em DPC++, de forma a atender ou mesmo tirar proveito das particularidades do projeto. Estas técnicas foram comprovadas, na prática, com a obtenção de resultados que ratificam a validade do modelo. Adicionalmente, novas técnicas estão sendo introduzidas de forma a aumentar ainda mais o nível de tolerância à ocorrência de falhas suportado em DPC++.

O trabalho expôs também as dificuldades, em relação à tolerância a falhas, originadas pelas novas características a serem introduzidas no projeto. Buscando um melhor desempenho global para as aplicações DPC++, o projeto está sendo incorporado de técnicas modernas de programação paralela, porém trazendo como efeito colateral a invalidação do modelo de tolerância a falhas atualmente suportado.

As perspectivas do projeto incluem a completa incorporação do modelo de replicação do objeto Diretório e um estudo aprofundado de viabilidade de alteração no esquema de criação de checkpoints distribuídos, de forma a suportar corretamente o novo modelo funcional baseado em multithreading.

References

CAV95
 CAVALHEIRO, Gerson G. H.; KRUG, Ricardo C.; RIGO, Sandro J.; NAVAUX, Philippe O. A. DPC++: an object-oriented distributed language. In: CONFERENCIA INTERNACIONAL DE LA SOCIEDAD CHILENA DE CIENCIA DE LA COMPUTACIÓN, 15., 1995, Arica, CL. Actas... Santiago: Sociedad Chilena de Ciencia de la Computación, 1995. p.92-103.
CAV93
 CAVALHEIRO, Gerson G. H.; NAVAUX, Philippe O. A. DPC++: uma linguagem para processamento distribuído. In: SIMPÓSIO BRASILEIRO DE ARQUITETURA DE COMPUTADORES -- PROCESSAMENTO DE ALTO DESEMPENHO, 5., 1993, Florianópolis. Anais... Florianópolis: SBC, 1993. v.2, p.732-744.
JAL94
 JALOTE, Pankaj. Fault tolerance in distributed systems. Englewood Cliffs: PTR Prentice Hall, 1994. 432p.
PIL97
 PILLA, Maurício Lima; BARRETO, Marcos Ennes; SANTOS, Rafael R. dos; CAVALHEIRO, Gerson G. H.; NAVAUX, Philippe O. A. Mecanismo de tolerância a falhas para a linguagem distribuída DPC++. In: SIMPÓSIO BRASILEIRO DE ARQUITETURA DE COMPUTADORES -- PROCESSAMENTO DE ALTO DESEMPENHO, 9., 1997, Campos do Jordão. Anais... São Paulo: Escola Politécnica da USP, 1997. p.139-152.
PIL97a
 PILLA, Maurício Lima; BARRETO, Marcos Ennes; SANTOS, Rafael R. dos; CAVALHEIRO, Gerson G. H.; NAVAUX, Philippe O. A. Implementação de um algortimo de criação de checkpoints para a linguagem distribuída DPC++. In: CONGRESO ARGENTINO DE CIENCIAS DE LA COMPUTACIÓN, 3., 1997, La Plata, AR. Anales... La Plata: Universidad Nacional de La Plata, 1997.
SAN96
 SANTOS, Rafael Ramos dos; CAVALHEIRO, Gerson G. H.; NAVAUX, Philippe O. A. Checkpoints distribuídos em DPC++. In: CONGRESO ARGENTINO DE CIENCIAS DE LA COMPUTACIÓN, 2., 1996, San Luis, AR. [Trabajos Seleccionados]... San Luis: Universidad Nacional de San Luis, 1996. p.74-85.
...DPC++
Naturalmente, a visão apresentada aqui é simplista, pois, na prática, a estrutura funcional de DPC++ é ligeiramente mais complexa. Contudo, essa visão não é incorreta, sendo apenas tomada de uma perspectiva mais elevada, com o intuito de fornecer os subsídios necessários ao entendimento do trabalho. Informações mais detalhadas acerca do projeto podem ser obtidas na bibliografia apresentada.

next up previous
Rafael Bohrer Avila

Mon Jul 6 16:17:13 EST 1998