mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 01:21:03 +01:00
227 lines
8.8 KiB
JavaScript
227 lines
8.8 KiB
JavaScript
// Tests that the last vote document is stored during elections and that it is loaded and used on
|
|
// startup.
|
|
//
|
|
// The test first runs a few elections and checks that the lastVote document is set correctly
|
|
// after each one.
|
|
//
|
|
// The test then restarts one node as a standalone, changes its last vote doc, and stops the
|
|
// other node. It then restarts the first node as a replicaset and manually runs
|
|
// replSetRequestVotes commands against it and checks that its response is correct.
|
|
//
|
|
// @tags: [requires_persistence]
|
|
|
|
(function() {
|
|
"use strict";
|
|
load("jstests/replsets/rslib.js"); // For getLatestOp()
|
|
|
|
var name = "last_vote";
|
|
var rst = new ReplSetTest({
|
|
name: name,
|
|
nodes: 2,
|
|
});
|
|
rst.startSet();
|
|
|
|
// Lower the election timeout to make the test run faster since it waits for multiple elections.
|
|
var conf = rst.getReplSetConfig();
|
|
conf.settings = {
|
|
electionTimeoutMillis: 6000,
|
|
};
|
|
rst.initiate(conf);
|
|
|
|
const lastVoteNS = 'local.replset.election';
|
|
|
|
function getLastVoteDoc(conn) {
|
|
assert.eq(
|
|
conn.getCollection(lastVoteNS).find().itcount(), 1, 'last vote should be singleton');
|
|
return conn.getCollection(lastVoteNS).findOne();
|
|
}
|
|
|
|
function setLastVoteDoc(conn, term, candidate) {
|
|
var newLastVote = {term: term, candidateIndex: rst.getNodeId(candidate)};
|
|
return assert.writeOK(conn.getCollection(lastVoteNS).update({}, newLastVote));
|
|
}
|
|
|
|
function assertNodeHasLastVote(node, term, candidate) {
|
|
var lastVoteDoc = getLastVoteDoc(node);
|
|
assert.eq(lastVoteDoc.term, term, node.host + " had wrong last vote term.");
|
|
assert.eq(lastVoteDoc.candidateIndex,
|
|
rst.getNodeId(candidate),
|
|
node.host + " had wrong last vote candidate.");
|
|
}
|
|
|
|
function assertCurrentTerm(node, term) {
|
|
var stat = assert.commandWorked(node.adminCommand({replSetGetStatus: 1}));
|
|
assert.eq(stat.term, term, "Term changed when it should not have");
|
|
}
|
|
|
|
jsTestLog("Test that last vote is set on successive elections");
|
|
|
|
// Run a few successive elections, alternating who becomes primary.
|
|
var numElections = 3;
|
|
for (var i = 0; i < numElections; i++) {
|
|
var primary = rst.getPrimary();
|
|
var secondary = rst.getSecondary();
|
|
var term = getLatestOp(primary).t;
|
|
|
|
// SERVER-20844 ReplSetTest starts up a single node replica set then reconfigures to the
|
|
// correct size, so secondaries didn't vote in the first election.
|
|
if (i > 0) {
|
|
jsTestLog("Last vote should have term: " + term + " and candidate: " + primary.host +
|
|
", index: " + rst.getNodeId(primary));
|
|
rst.nodes.forEach(function(node) {
|
|
assertNodeHasLastVote(node, term, primary);
|
|
});
|
|
}
|
|
assert.commandWorked(primary.adminCommand({replSetStepDown: 60 * 10, force: true}));
|
|
|
|
// Make sure a new primary has been established.
|
|
rst.waitForState(primary, ReplSetTest.State.SECONDARY);
|
|
rst.waitForState(secondary, ReplSetTest.State.PRIMARY);
|
|
|
|
// Reset election timeout for the old primary.
|
|
assert.commandWorked(primary.adminCommand({replSetFreeze: 0}));
|
|
}
|
|
|
|
var term = getLatestOp(rst.getPrimary()).t + 100;
|
|
|
|
jsTestLog("Test that last vote is loaded on startup");
|
|
|
|
// Ensure that all ops are replicated before stepping up node 1.
|
|
rst.awaitReplication();
|
|
|
|
// We cannot reconfig node 0 to have priority 0 if it is currently the primary,
|
|
// so we make sure node 1 is primary.
|
|
jsTestLog("Stepping up node 1");
|
|
rst.stepUp(rst.nodes[1]);
|
|
|
|
jsTestLog("Reconfiguring cluster to make node 0 unelectable so it stays SECONDARY on restart");
|
|
conf = rst.getReplSetConfigFromNode();
|
|
conf.version++;
|
|
conf.members[0].priority = 0;
|
|
reconfig(rst, conf);
|
|
rst.awaitNodesAgreeOnConfigVersion();
|
|
|
|
jsTestLog("Restarting node 0 as a standalone");
|
|
var node0 = rst.restart(0, {noReplSet: true}); // Restart as a standalone node.
|
|
jsTestLog("Stopping node 1");
|
|
rst.stop(1); // Stop node 1 so that node 0 controls the term by itself.
|
|
jsTestLog("Setting the lastVote on node 0 to term: " + term + " candidate: " +
|
|
rst.nodes[0].host + ", index: 0");
|
|
setLastVoteDoc(node0, term, rst.nodes[0]);
|
|
|
|
jsTestLog("Restarting node 0 in replica set mode");
|
|
node0 = rst.restart(0); // Restart in replSet mode again.
|
|
rst.waitForState(node0, ReplSetTest.State.SECONDARY);
|
|
|
|
assert.soonNoExcept(function() {
|
|
assertCurrentTerm(node0, term);
|
|
return true;
|
|
});
|
|
|
|
jsTestLog("Manually sending node 0 a dryRun replSetRequestVotes command, " +
|
|
"expecting failure in old term");
|
|
var response = assert.commandWorked(node0.adminCommand({
|
|
replSetRequestVotes: 1,
|
|
setName: name,
|
|
dryRun: true,
|
|
term: term - 1,
|
|
candidateIndex: 1,
|
|
configVersion: conf.version,
|
|
lastCommittedOp: getLatestOp(node0)
|
|
}));
|
|
assert.eq(response.term,
|
|
term,
|
|
"replSetRequestVotes response had the wrong term: " + tojson(response));
|
|
assert(!response.voteGranted,
|
|
"node granted vote in term before last vote doc: " + tojson(response));
|
|
assertNodeHasLastVote(node0, term, rst.nodes[0]);
|
|
assertCurrentTerm(node0, term);
|
|
|
|
jsTestLog("Manually sending node 0 a dryRun replSetRequestVotes command in same term, " +
|
|
"expecting success but no recording of lastVote");
|
|
response = assert.commandWorked(node0.adminCommand({
|
|
replSetRequestVotes: 1,
|
|
setName: name,
|
|
dryRun: true,
|
|
term: term,
|
|
candidateIndex: 1,
|
|
configVersion: conf.version,
|
|
lastCommittedOp: getLatestOp(node0)
|
|
}));
|
|
assert.eq(response.term,
|
|
term,
|
|
"replSetRequestVotes response had the wrong term: " + tojson(response));
|
|
assert(response.voteGranted,
|
|
"node failed to grant dryRun vote in term equal to last vote doc: " + tojson(response));
|
|
assert.eq(response.reason,
|
|
"",
|
|
"replSetRequestVotes response had the wrong reason: " + tojson(response));
|
|
assertNodeHasLastVote(node0, term, rst.nodes[0]);
|
|
assertCurrentTerm(node0, term);
|
|
|
|
jsTestLog(
|
|
"Manually sending node 0 a replSetRequestVotes command, expecting failure in same term");
|
|
response = assert.commandWorked(node0.adminCommand({
|
|
replSetRequestVotes: 1,
|
|
setName: name,
|
|
dryRun: false,
|
|
term: term,
|
|
candidateIndex: 1,
|
|
configVersion: conf.version,
|
|
lastCommittedOp: getLatestOp(node0)
|
|
}));
|
|
assert.eq(response.term,
|
|
term,
|
|
"replSetRequestVotes response had the wrong term: " + tojson(response));
|
|
assert(!response.voteGranted,
|
|
"node granted vote in term of last vote doc: " + tojson(response));
|
|
assertNodeHasLastVote(node0, term, rst.nodes[0]);
|
|
assertCurrentTerm(node0, term);
|
|
|
|
jsTestLog("Manually sending node 0 a replSetRequestVotes command, " +
|
|
"expecting success with a recording of the new lastVote");
|
|
response = assert.commandWorked(node0.adminCommand({
|
|
replSetRequestVotes: 1,
|
|
setName: name,
|
|
dryRun: false,
|
|
term: term + 1,
|
|
candidateIndex: 1,
|
|
configVersion: conf.version,
|
|
lastCommittedOp: getLatestOp(node0)
|
|
}));
|
|
assert.eq(response.term,
|
|
term + 1,
|
|
"replSetRequestVotes response had the wrong term: " + tojson(response));
|
|
assert(response.voteGranted,
|
|
"node failed to grant vote in term greater than last vote doc: " + tojson(response));
|
|
assert.eq(response.reason,
|
|
"",
|
|
"replSetRequestVotes response had the wrong reason: " + tojson(response));
|
|
assertNodeHasLastVote(node0, term + 1, rst.nodes[1]);
|
|
assertCurrentTerm(node0, term + 1);
|
|
|
|
jsTestLog("Manually sending node 0 a dryRun replSetRequestVotes command in future term, " +
|
|
"expecting success but no recording of lastVote");
|
|
response = assert.commandWorked(node0.adminCommand({
|
|
replSetRequestVotes: 1,
|
|
setName: name,
|
|
dryRun: true,
|
|
term: term + 2,
|
|
candidateIndex: 1,
|
|
configVersion: conf.version,
|
|
lastCommittedOp: getLatestOp(node0)
|
|
}));
|
|
assert.eq(response.term,
|
|
term + 2,
|
|
"replSetRequestVotes response had the wrong term: " + tojson(response));
|
|
assert(response.voteGranted,
|
|
"node failed to grant vote in term greater than last vote doc: " + tojson(response));
|
|
assert.eq(response.reason,
|
|
"",
|
|
"replSetRequestVotes response had the wrong reason: " + tojson(response));
|
|
assertNodeHasLastVote(node0, term + 1, rst.nodes[1]);
|
|
assertCurrentTerm(node0, term + 2);
|
|
|
|
rst.stopSet();
|
|
})();
|