MongoDB – Como funciona a eleição
- Postado por Adriano Bonacin
- Categorias mongodb
- Data 21/01/2022
- Comentários 0 comentário
Fala pessoal, espero que estejam bem. Nos nossos últimos artigos falamos como obter o status do nosso ReplicaSet e assim descobrir quem são seus membros. Em seguida, vimos como trocar os papéis entre Primary e Secondary. Agora vamos entender como funciona a alta disponibilidade no MongoDB. Tudo se baseia em um processo chamado eleição.
De modo simples, podemos dizer que é o processo de escolha do novo Primary. Este processo é disparado na criação do ReplicaSet (rs.initiate()), quando ocorre algum evento como falha do atual Primary ou mesmo quando a configuração recebe um comando como rs.stepDown() ou rs.reconfig().
Limite de votos
Por default, cada membro do ReplicaSet contribui com um voto. E para que seja eleito um novo Primary, é preciso que a maioria vote. Falamos no artigo em que criamos nosso primeiro ReplicaSet que podemos ter até 50 membros. Outra restrição é que podemos ter no máximo 7 votos. Desta forma, se sua configuração possui mais de 7 membros, alguns deles não poderão votar.
Vence a maioria
Vence a eleição algum host que consegue contato com a maioria. Este é um ponto muito importante. Para ilustrar, mantivemos até agora nosso ReplicaSet com apenas dois membros. Isto quer dizer que se perdermos um host, perdemos a maioria. E sem a maioria viva, nosso primary dispara automaticamente um stepDown e não temos mais um node Primary.
Percebeu? Com dois hosts, não podemos perder nenhum. Vamos ver como funciona. Se você chegou até aqui seguindo nossos artigos, teremos:
rsYadax0 [direct: primary] test> rs.status().members
[
{
_id: 0,
name: 'mymongo1:27017',
health: 1,
state: 2,
stateStr: 'SECONDARY',
uptime: 333219,
optime: { ts: Timestamp({ t: 1642790021, i: 1 }), t: Long("2") },
optimeDurable: { ts: Timestamp({ t: 1642790021, i: 1 }), t: Long("2") },
optimeDate: ISODate("2022-01-21T18:33:41.000Z"),
optimeDurableDate: ISODate("2022-01-21T18:33:41.000Z"),
lastAppliedWallTime: ISODate("2022-01-21T18:33:41.962Z"),
lastDurableWallTime: ISODate("2022-01-21T18:33:41.962Z"),
lastHeartbeat: ISODate("2022-01-21T18:33:43.568Z"),
lastHeartbeatRecv: ISODate("2022-01-21T18:33:43.972Z"),
pingMs: Long("0"),
lastHeartbeatMessage: '',
syncSourceHost: 'mymongo2:27017',
syncSourceId: 1,
infoMessage: '',
configVersion: 3,
configTerm: 2
},
{
_id: 1,
name: 'mymongo2:27017',
health: 1,
state: 1,
stateStr: 'PRIMARY',
uptime: 445832,
optime: { ts: Timestamp({ t: 1642790021, i: 1 }), t: Long("2") },
optimeDate: ISODate("2022-01-21T18:33:41.000Z"),
lastAppliedWallTime: ISODate("2022-01-21T18:33:41.962Z"),
lastDurableWallTime: ISODate("2022-01-21T18:33:41.962Z"),
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
electionTime: Timestamp({ t: 1642706899, i: 1 }),
electionDate: ISODate("2022-01-20T19:28:19.000Z"),
configVersion: 3,
configTerm: 2
self: true,
lastHeartbeatMessage: ''
}
]
Agora mantenha a sessão do mongosh no mymongo2 (ou do host que for seu primary) e vamos derrubar o Secondary no mymongo1.
[root@mymongo1 /]# ps -ef | grep mongod | grep -v grep
root 170 1 1 Jan16 ? 01:32:06 mongod -f /etc/mongod.conf
[root@mymongo1 /]# kill -15 170
[root@mymongo1 /]
O log do Primary imediatamente apresenta:
{"t":{"$date":"2022-01-21T18:38:38.975+00:00"},"s":"I", "c":"-", "id":4333222, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"RSM received error response","attr":{"host":"mymongo1:27017","error":"ShutdownInProgress{ remainingQuiesceTimeMillis: 14992 }: The server is in quiesce mode and will shut down","replicaSet":"rsYadax0","response":"{}"}}
{"t":{"$date":"2022-01-21T18:38:38.976+00:00"},"s":"I", "c":"NETWORK", "id":4712102, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"Host failed in replica set","attr":{"replicaSet":"rsYadax0","host":"mymongo1:27017","error":{"code":91,"codeName":"ShutdownInProgress","errmsg":"The server is in quiesce mode and will shut down","remainingQuiesceTimeMillis":14992},"action":{"dropConnections":false,"requestImmediateCheck":false}}}
E a cada segundo há mais reclamação no log. O erro connection refused é bastante comum no dia a dia de um ambiente com problemas. Ele indica que o serviço do mongod parou de ouvir conexões (aqui foi porque matamos o processo mongod).
{"t":{"$date":"2022-01-21T19:07:14.906+00:00"},"s":"I", "c":"REPL_HB", "id":23974, "ctx":"ReplCoord-172","msg":"Heartbeat failed after max retries","attr":{"target":"mymongo1:27017","maxHeartbeatRetries":2,"error":{"code":6,"codeName":"HostUnreachable","errmsg":"Error connecting to mymongo1:27017 (172.100.0.11:27017) :: caused by :: Connection refused"}}}
Aquela sessão que ficou aberta, se você pressionar <ENTER>, verá que mudou para Secondary. Isso quer dizer que em cercas circunstâncias o node continua servindo leitura (vamos falar disso outro artigo), mas as escritas começam falhar.
rsYadax0 [direct: primary] test>
rsYadax0 [direct: secondary] test>
Eleição
Agora vamos startar novamente o mymongo1 e ver que é disparado um processo de eleição. Provavelmente será vencido pelo mymongo2, que é quem ficou vivo mais tempo.
[root@mymongo1 /]# mongod -f /etc/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 906
child process started successfully, parent exiting
[root@mymongo1 /]#
Uma vez startado, o log registra:
mymongo1:
{"t":{"$date":"2022-01-21T19:18:13.945+00:00"},"s":"I", "c":"CONTROL", "id":20698, "ctx":"-","msg":"***** SERVER RESTARTED *****"}
mymongo2
{"t":{"$date":"2022-01-21T19:18:16.877+00:00"},"s":"I", "c":"ELECTION", "id":4615652, "ctx":"ReplCoord-171","msg":"Starting an election, since we've seen no PRIMARY in election timeout period","attr":{"electionTimeoutPeriodMillis":10000}}
{"t":{"$date":"2022-01-21T19:18:16.877+00:00"},"s":"I", "c":"ELECTION", "id":21438, "ctx":"ReplCoord-171","msg":"Conducting a dry run election to see if we could be elected","attr":{"currentTerm":2}}
..
{"t":{"$date":"2022-01-21T19:18:16.888+00:00"},"s":"I", "c":"ELECTION", "id":21450, "ctx":"ReplCoord-173","msg":"Election succeeded, assuming primary role","attr":{"term":3}}
{"t":{"$date":"2022-01-21T19:18:16.888+00:00"},"s":"I", "c":"REPL", "id":21358, "ctx":"ReplCoord-173","msg":"Replica set state transition","attr":{"newState":"PRIMARY","oldState":"SECONDARY"}}
Em questão de segundos, temos o nosso eleito assumindo o papel de primary. Tudo voltou ao normal, mas como podemos fazer para que a gente possa perder um node e a vida continuar? A resposta é simples: tendo mais nodes.
Se temos três nodes e perdemos um, ainda sobra dois, que é a maioria. Vamos fazer esse teste no próximo artigo. Até lá.
Tag:mongodb