Alta disponibilidade e tolerância a falhas com MongoDB

Publicado por Thiago D. Chiarato no dia dev

Um ponto fundamental para o sucesso de uma plataforma SaaS é sua credibilidade. Utilizar uma ferramenta instável, ou que passe grande parte do tempo inacessível, gera insatisfação e queda no nível de engajamento do usuário.

Para criarmos um sistema que seja tolerante a falhas, podemos iniciar pelo banco de dados. Vou mostrar a seguir como funciona a replicação de informações entre instâncias do MongoBD e como podemos evitar a indisponibilidade das nossas informações.

Overview

Um replset ou ReplicaSet pode ser descrito como um processo de sincronização de dados em múltiplos servidores. Este processo de replicação aumenta a capacidade que o sistema tem de se recuperar de falhas e perda de dados. Em poucas palavras, são um conjunto de instâncias mongod que hospedam o mesmo dataset. Em alguns casos o uso de replsets tem o objetivo de aumentar a capacidade de leitura de dados.

Arquitetura

Existem três pontos importantes na arquitetura de um replset que são necessários para o entendimento desta feature, são eles os primary nodes, secondary nodes e os arbiters. Abaixo, um breve detalhamento sobre cada um deles:

  • Primary Node

    O node primário é o unico membro responsável por receber todos os comandos de escrita. Após a inserção dos dados, todos os registros desta operação são inseridos no oplog primário.

  • Secondary Nodes

    Os nodes secundários replicam as informações do node primário buscando estes dados no oplog primário. Nodes secundários também podem executar comandos de leitura, porém por padrão todas essas operações são dirigidas ao node primário. Para redirecionar estes comandos aos nodes secundários, basta alterar as preferências de leitura.

ReplicaSet com os nodes primário e secundários

Imagem por http://docs.mongodb.org/

  • Arbiters

    O Arbiter não replica as informações dos nodes primários, porém, exerce um papel importante, o de desempate. Sempre que uma eleição terminar empatada o arbiter terá o voto de minerva para decidir qual será o node primário.

ReplicaSet com os nodes primário, secundário e arbiter

Imagem por http://docs.mongodb.org/

Um replset pode ter até 12 nodes, porém, apenas 7 deles podem votar. Conhecidos como non-voting members, os nodes que não votam replicam as informações do node primário e podem se tornar um node primário em uma eleição.

Replicação assíncrona

Realizar operações de leitura em nós secundários pode resultar em uma informação que não reflete os dados do primário, isso porque a replicação dos dados acontece de forma assíncrona.

Falha e Eleição

Cada node monitora a “saúde” de seu companheiro através do heartbeat, caso esta comunicação falhe por mais de 10 segundos, o replset tentará eleger um novo nó primário.

Requisitos

Os requisitos para a criação de um replset são:

  • No mínimo 3 nodes;
  • e o número ímpar de instâncias (não é um requisito, mas uma boa prática)

O replset somente funciona se a maioria de nodes elegíveis estiver funcionando, caso contrário, ele deixará de responder a comandos write e irá operar somente readonly. Sendo assim, três é o número mínimo de nodes necessários para se criar um replset que possa se recuperar caso uma das instâncias venha a falhar. Em relação ao número ímpar de instâncias, há uma explicação simples, se houver um número par de instâncias, haverá uma possibilidade maior de empate na eleição.

Iniciando com ReplicaSets

Abaixo vamos criar um ambiente simples com três nodes que exemplifica o funcionamento da replicação dos dados. Para isso, criei um arquivo chamado `create_replset.sh onde se encontra a criação de três instâncias mongod.

1
2
3
4
#!/bin/bash
mongod --replSet replica_set --port 27017 --dbpath /data/rs1 --logpath /data/rs1/log.txt --fork
mongod --replSet replica_set --port 27018 --dbpath /data/rs2 --logpath /data/rs1/log.txt --fork
mongod --replSet replica_set --port 27019 --dbpath /data/rs3 --logpath /data/rs1/log.txt --fork

As instâncias foram criadas com o mesmo nome replica_set e em portas distintas. O comando --fork é necessário para não travar o terminal ao final de cada comando.

Para executar este arquivo, podemos utilizar o comando abaixo:

1
$ sudo bash create_replset.sh

Após ter as ter instâncias “up and running” vamos às configurações. Para isso, criei o arquivo `init.js com o código abaixo:

1
2
3
4
5
6
7
8
9
10
11
config = {
  _id: "replica_set",
  members: [
      { _id: 0, host: "localhost:27017" },
      { _id: 1, host: "localhost:27018" },
      { _id: 2, host: "localhost:27019" }
  ]
}

rs.initiate(config);
rs.status();

Neste arquivo podemos ver uma referência ao nome do replsets (exatamente o mesmo nome informado anteriormente) e quais são seus membros.

Podemos executar este arquivo com o comando:

1
$ mongo localhost:27017 < init.js

Feito isso, nosso replset está pronto para uso. Se logarmos em qualquer uma dessas instâncias mongod, podemos ver a indicação se o mesmo é um nó primário ou secundário, como mostra a imagem abaixo:

1
$ mongo --port 27017
Instância mongod exibindo o nó primário

Com o comando abaixo podemos ver o status de cada instância:

1
rs.status()

Em seguida, podemos “matar” o node primário para ver o funcionamento da eleição. Um novo nó assumirá o posto de primário:

1
rs.shutdownServer()

Com isso, temos nosso sistema de instâncias servindo a um nó primário e tolerante a falhas. Espero ter ajudado e mostrado o caminho das pedras para você. ;)

Até a próxima! \o/

Thiago D. Chiarato

Thiago D. Chiarato

Full Stack Engineer

Comentários