3.4 Decisões de projeto e considerações
3.4.2 Cassandra para gerenciamento do armazenamento
Muitos dos desafios enfrentados quanto ao desenvolvimento de um sistema de arma- zenamento distribuído são delegados para serem resolvidos através do gerenciamento
3.4. DECISÕES DE PROJETO E CONSIDERAÇÕES
executado pelo Cassandra. Alguns desses desafios são citados no artigo sobre o Dynamo, entre eles, robustez e escalabilidade no balanceamento de carga, detecção de filiação e de falhas, recuperação de falhas, concorrência e agendamento de tarefas (jobs), e empa- cotamento de requisições [8]. Além desses, existem outros desafios como, garantia de disponibilidade e consistência dos arquivos, replicação dos dados e tamanho e quantidade das mensagens trafegadas. Nesta seção será discutida a forma como o Cassandra resolve todos esses problemas.
O Cassandra foi desenvolvido em uma arquitetura peer-to-peer, também conhecida como P2P, que é uma das arquiteturas comuns existentes na internet, além dela, a outra arquitetura muito usada é a Cliente/Servidor. Na arquitetura Cliente/Servidor, existe no mínimo duas entidades, uma que requisita um recurso e outra que atende a essa requisição, nessa arquitetura os nós agem de uma forma ou de outra, nunca as duas ao mesmo tempo. Na arquitetura P2P as entidades se comportam tanto como clientes quanto como servidores, consumindo e oferecendo recursos na rede.
Schollmeier[38] define P2P como uma rede na qual os nós compartilham parte dos seus recursos de hardware, entre eles, processamento, capacidade de armazenamento, impressora, entre outros. Esses recursos compartilhados são essenciais para os serviços e conteúdos oferecidos pela rede, como, por exemplo, compartilhamento de arquivos.
A arquitetura P2P é dividida em dois tipos, as chamadas arquitetura P2P pura e híbrida. [38] define a arquitetura P2P pura se ela está classificada dentro definição anterior e, além disso, se qualquer nó da rede puder ser removido sem que a rede sofra qualquer perda. O mesmo trabalho define que uma arquitetura P2P é dita híbrida quando ela está classificada na primeira definição de P2P e, além disso, uma entidade central é necessária para fornecer parte do serviço que a rede se dispõe a oferecer [38].
No Cassandra, a arquitetura P2P usada foi a pura. Todo nó no Cassandra é idêntico aos outros, não existe nó mestre e nem nó escravo. Com essa estrutura, a escalabilidade horizontal se torna mais fácil, já que só é preciso adicionar um novo nó no cluster para que ele se integre e se organize dentro da rede, para isso, ele possui um tempo para aprender como está a topologia do anel (a rede do Cassandra funciona como um anel). Depois da descoberta de como a rede está topologicamente estruturada o nó começa a receber como qualquer outro que integra a rede.
O Cassandra utiliza o próprio sistema para armazenar as informações de configurações dele. Todas essas informações são armazenadas dentro do Keyspace chamado system. Entre essas informações estão o token do nó, o nome do cluster, definições de schema, entre outras. Esse Keyspace não pode ser modificado pelo usuário.
3.4. DECISÕES DE PROJETO E CONSIDERAÇÕES
Tolerância à falhas
O Cassandra usa o gossip protocol[23] para a detecção de falhas. Periodicamente, uma mensagem é enviada para um outro nó, quando o nó recebe a mensagem ele responde com um Ack, quando a mensagem é recebida pelo nó que a iniciou, ele responde com um Ack2. Com a utilização desse protocolo é possível identificar os nós que pararam de funcionar. O Cassandra ainda usa um algoritmo que ao invés de apenas informar se um sistema é ou não confiável, ele retorna um valor que é o nível de suspeita, esse algoritmo é chamado de Phi Accrual Failure Detection [9]. O Cassandra possui um método que ajuda na decisão se um nó está morto ou não, baseado no valor do nível de suspeita, com assinatura interpret(InetAddress), onde InetAddress representa o endereço do nó.
Recuperação de falhas
Todo sistema que utiliza gossip protocol deve ter um mecanismo para tratar os problemas decorrentes da sua utilização, o principal deles é chamado de anty-entropy e é um mecanismo para sincronização de réplicas, que assegura que os valores das réplicas armazenados nos nós estejam atualizados com a versão mais recente. Durante a fase de compactação, os nós fazem a requisição das árvores Merkle dos vizinhos para poder comparar com a sua árvore e assim poder identificar possíveis valores desatualizados. A árvore Merkle funciona como um snapshot da família de colunas atual, ela é uma árvore que possui os valores dos hashes dos dados armazenados nas famílias de colunas, esses valores são comparados com os do nó requisitante, caso for encontrado algum valor diferente, é feito o reparo nos dados atualizando-os para a versão mais recente [38]. Anti-entropy também é usado no Dynamo [8], mas o Cassandra faz um uso diferente, na implementação do Cassandra existe uma árvore Merkle para cada família de colunas. O algoritmo de anti-entropy é executado depois de cada atualização no banco de dados.
As operações de escrita no Cassandra são feitas primeiramente em um log que é chamado de commit log. Este mecanismo tem como finalidade possibilitar a recuperação de uma escrita mal sucedida. A escrita só é marcada como feita quando a operação é salva no commit log, com ele é possível recuperar uma falha na escrita feita em memória. Após a escrita no commit log o dado é salvo em uma estrutura de dados alocada na memória que é chamada de Memtable. O Memtable só terá os dados salvos no disco quando ele atingir um limite de objetos armazenados. A estrutura aonde os dados são salvos no disco é chamada de SSTable.
3.4. DECISÕES DE PROJETO E CONSIDERAÇÕES
Read.ONE Read.QUORUM Read.ALL
Write.ANY Weak Weak Weak
Write.ONE Weak Weak Strong
Write.QUORUM Weak Strong Strong
Write.ALL Strong Strong Strong
Tabela 3.1 Força do consistência baseada nos níveis das operações [7]
Consistência
A consistência dos dados que são armazenados no Cassandra tem o seu nível definido pelo usuário. A consistência pode ser usada como valor ONE (consistência fraca), nesse nível de consistência o Cassandra pode retornar o dado encontrado no primeiro nó consultado sem verificar se aquele é o valor mais recente. No caso de existir muitos clientes na rede, é recomendável ter um nível de consistência no mínimo QUORUM, que é um nível no qual o Cassandra precisa ler de vários nós antes de retornar o valor, o que assegura que o sistema encontrará o valor do dado mais recente. No nível de consistência ALL, o Cassandra consulta os dados armazenados em todos os nós antes de retornar o valor. Se uma das consistências fortes forem utilizadas (QUORUM ou ALL), a recuperação de falhas, conhecida como read-repair é executa antes do dado ser retornado.
O CAP Theorem descreve um sistema distribuído com relação à três aspectos: consis- tência, disponibilidade e tolerância à partição. O teorema diz que é impossível que um sistema distribuído possua os três aspectos ao mesmo tempo, sempre um desses aspectos tem que ser sacrificado. O Cassandra tem flexibilidade e permite que o usuário escolha os aspectos do CAP Theorem que estão no sistema [7]. A Tabela3.1mostra qual o nível de consistência do Cassandra a depender das escolhas do usuário.
O quorum é o número de nós que deve ser consultado para se chegar a um consenso sobre a validade do dado (pode ser ANY, ONE, QUORUM ou ALL). Se o valor do quorum for ONE, o dado só será armazenado em um único nó, o que vai torná-lo sempre consistente, mas em contrapartida, ele nunca será replicado, o que diminui a disponibilidade do mesmo.
Disponibilidade
O Cassandra possui um mecanismo para disponibilidade de escrita mesmo quando o nó que receberia a mensagem tem uma falha de hardware, ou qualquer outra que o impeça de receber a mensagem, esse mecanismo é chamado de hinted handoff. Quando um nó tenta enviar uma mensagem para um outro que não pode ser alcançado por alguma falha,
3.4. DECISÕES DE PROJETO E CONSIDERAÇÕES
o nó remetente grava a mensagem em uma área do keyspace system para que quando o nó onde a mensagem deveria ser escrita voltar, ela possa ser enviada e persistida.
Empacotamento de requisições
A troca de mensagens entre os nós é feita através de um serviço. As mensagens que chegam são encaminhadas para um pool de threads, o qual é responsável pelo gerenciamento delas. Em seguida, é analisado se a mensagem é do tipo streaming, que é uma forma eficiente utilizada pelo Cassandra para fazer a transferência de SSTable entre os nós, ou se é uma mensagem serializada padrão, e a partir disso é determinado para qual manipulador ela será atribuída.