A replicação no MongoDB envolve conjuntos de réplicas por membros com uma arquitetura de membros primários e secundários, mas às vezes com um membro não portador de dados chamado árbitro. O processo de replicação é que, sempre que os dados são gravados no nó primário, as alterações são registradas em um arquivo oplog do qual os membros secundários aplicam as mesmas alterações. As operações de leitura podem ser feitas a partir de qualquer membro portador de dados, criando assim um cenário comumente conhecido como Alta Disponibilidade.
No entanto, em alguns casos, os membros secundários podem falhar em alcançar o primário ao fazer alterações e, caso o nó primário falhe antes que essas alterações sejam aplicadas, será forçado a ressincronizar todo o cluster para que possam estar no mesmo estado de dados.
O que é uma reversão?
Este é um recurso de failover automático no MongoDB, em que o nó primário em um conjunto de réplicas pode falhar ao fazer alterações que, infelizmente, acabam não sendo refletidas para os membros secundários a tempo do oplog, portanto, é necessário reverter o estado do primário para um antes das alterações serem feitas.
As reversões são, portanto, necessárias apenas quando o primário aceita gravar as operações que não foram replicadas para os membros secundários antes que o primário seja desativado por algum motivo, como partição de rede. Caso as operações de escrita consigam ser replicadas em um dos membros que esteja disponível e acessível à maioria do conjunto de réplicas, não ocorrerá um rollback.
A principal razão por trás dos rollbacks no MongoDB é manter a consistência dos dados para todos os membros e, portanto, quando o primário voltar ao conjunto de réplicas, se suas alterações não tiverem sido aplicadas aos membros secundários, ele será revertido para o estado antes da falha.
No entanto, rollbacks devem ser raros ou evitados no MongoDB, pois podem resultar em muita perda de dados e, consequentemente, afetar a operação de aplicativos conectados ao banco de dados.
Processo de reversão do MongoDB
Vamos considerar um conjunto de réplicas de três membros com A como o principal, B e C como os membros secundários. Estaremos preenchendo dados para A e, ao mesmo tempo, acionando alguns particionamentos de rede para B e C. Usaremos o MongoDB versão 4.2 e o Atlas neste teste.
Primeiro, obteremos o status da réplica definida executando o comando rs.status() no shell do mongo
MongoDB Enterprise Cluster0-shard-0:PRIMARY> rs.status()
Olhando para o atributo de membros, você pode ver algo como
"members" : [
{
"_id" : 0,
"name" : "cluster0-shard-00-00-sc27x.mongodb.net:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1891079,
"optime" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDurable" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDate" : ISODate("2020-07-15T15:25:11Z"),
"optimeDurableDate" : ISODate("2020-07-15T15:25:11Z"),
"lastHeartbeat" : ISODate("2020-07-15T15:25:19.509Z"),
"lastHeartbeatRecv" : ISODate("2020-07-15T15:25:18.532Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceHost" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 4
},
{
"_id" : 1,
"name" : "cluster0-shard-00-01-sc27x.mongodb.net:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1891055,
"optime" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDurable" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDate" : ISODate("2020-07-15T15:25:11Z"),
"optimeDurableDate" : ISODate("2020-07-15T15:25:11Z"),
"lastHeartbeat" : ISODate("2020-07-15T15:25:17.914Z"),
"lastHeartbeatRecv" : ISODate("2020-07-15T15:25:19.403Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceHost" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 4
},
{
"_id" : 2,
"name" : "cluster0-shard-00-02-sc27x.mongodb.net:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1891089,
"optime" : {
"ts" : Timestamp(1594826711, 1),
"t" : NumberLong(27)
},
"optimeDate" : ISODate("2020-07-15T15:25:11Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1592935644, 1),
"electionDate" : ISODate("2020-06-23T18:07:24Z"),
"configVersion" : 4,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
Isso mostrará o status de cada membro do seu conjunto de réplicas. Agora abrimos um novo terminal para o nó A e o preenchemos com 20.000 registros:
MongoDB Enterprise Cluster0-shard-0:PRIMARY> for (var y = 20000; y >= 0; y--) {
db.mytest.insert( { record : y } )
}
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.mytest 2020-07-15T21:28:40.436+2128 I NETWORK [thread1] trying reconnect to 127.0.0.1:3001 (127.0.0.1) failed
2020-07-15T21:28:41.436+2128 I
NETWORK [thread1] reconnect 127.0.0.1:3001 (127.0.0.1) ok
MongoDB Enterprise Cluster0-shard-0:SECONDARY> rs.slaveOk()
MongoDB Enterprise Cluster0-shard-0:SECONDARY> db.mytest.count()
20000
Durante o particionamento da rede, A ficará inativo tornando-o indisponível para B e C e, portanto, B eleito como primário em nosso caso. Quando A se juntar novamente, ele será adicionado como secundário e você pode verificar isso usando o comando rs.status(). No entanto, alguns registros conseguiram ser replicados para o membro B antes do particionamento da rede, como visto abaixo:(Lembre-se, neste caso, B é o primário agora)
MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.mytest.find({}).count()
12480
O número é a contagem de documentos que puderam ser replicados para B antes de A ficar inativo.
Se escrevermos alguns dados em B e permitirmos que A entre na rede, podemos notar algumas mudanças em A
connecting to: 127.0.0.1:3001/admin
MongoDB Enterprise Cluster0-shard-0:ROLLBACK>
MongoDB Enterprise Cluster0-shard-0:RECOVERING>
MongoDB Enterprise Cluster0-shard-0:SECONDARY>
MongoDB Enterprise Cluster0-shard-0:SECONDARY>
MongoDB Enterprise Cluster0-shard-0:PRIMARY>
Usando um oplogFetcher membros secundários sincronizam entradas de oplog de seu syncSource. O oplogFetcher aciona um método find para o oplog de origem seguido por uma série de séries de cursores getMores. Quando A se junta novamente como secundário, a mesma abordagem é aplicada e um documento maior que o carimbo de data/hora do predicado é retornado. Se o primeiro documento em B não corresponder à última entrada do oplog de A, A será forçado a um rollback.
Recuperando dados de rollback no MongoDB
Rollback não é uma coisa ruim no MongDB, mas deve-se tentar o máximo possível para garantir que eles não aconteçam com muita frequência. É uma medida automática de segurança para garantir a consistência dos dados entre os membros de um conjunto de réplicas. Caso a reversão aconteça, aqui estão algumas etapas para resolver a situação:
Reverter coleta de dados
Você precisa coletar dados de membros sobre a reversão. Isso é feito garantindo que os arquivos de reversão sejam criados (disponível apenas com o MongoDB versão 4.0) ativando o createRollbackDataFiles. Por padrão, esta opção é definida como verdadeira, portanto, os arquivos de reversão sempre serão criados.
Os arquivos de rollback são colocados no caminho Mongorestore é um aspecto vital do MongoDB que pode ajudar na recuperação de arquivos de dados de rollback. A primeira coisa é copiar os arquivos de rollback para um novo servidor e, em seguida, usar o mongorestore para carregar os arquivos em seu servidor. O comando mongorestore é mostrado abaixo. Esta etapa precisa de um critério para escolher entre os dados a serem mantidos dos arquivos de rollback e os dados a serem descartados. É aconselhável importar todos os dados dos arquivos de rollback, este ponto de decisão torna esta etapa a etapa mais difícil na recuperação de dados. Inicie a etapa final baixando os dados limpos por meio do uso de mongorestore e mongodump. Em seguida, reimporte os dados para o cluster de produção original. Para evitar que ocorram rollbacks de dados ao usar o MongoDB, pode-se fazer o seguinte. Isso pode ser feito usando w:major write concern, que tem o poder de opção de solicitação de confirmação que permitirá a operação de gravação para determinadas tags específicas de instâncias do Mongod. Isso pode ser feito usando a opção w seguida pela tag A versão atualizada do MongoDB , ou seja, a versão 4.2 tem a capacidade de desligar todas as operações em andamento no caso de uma reversão. A versão 4.2 da versão de compatibilidade de recursos do MongoDB (fcv) "4.2" é capaz de aguardar todos os índices em andamento que estão sendo criados e concluídos antes de uma reversão ocorrer Lugar, colocar. No entanto, a versão 4.0 aguarda o andamento contínuo e cria o índice em segundo plano, portanto, a possibilidade de uma reversão é alta. A versão 4.0 do MongoDB não tem limites listados de dados específicos que podem ser revertidos quando o índice de segundo plano em andamento é compilado.
Carregando os dados dos arquivos de reversão em um banco de dados ou servidor separado
mongorestore -u <> -p <> -h 127.0.0.1 -d <rollbackrestoretestdb> -c <rollbackrestoretestc> <path to the .bson file> --authenticationDatabase=<database of user>
Limpar dados desnecessários e filtrar dados
Usando o primário como um cluster para importar dados
Evitando reversões do MongoDB
Todos os membros votantes são 'MAJORITY'
Operações do usuário
Criações de índice
Tamanho e limitações
Conclusão
A reversão do MongoDB é um fenômeno comum para aqueles que usam o MongoDB sem o conhecimento de como evitá-lo. As reversões são evitáveis se seguirmos e aderirmos a algumas das práticas seguras e formas de evitar reversões no MongoDB. Ao todo, é sempre aconselhável atualizar para a versão mais recente do MongoDB para evitar alguns soluços evitáveis.