mirror of https://github.com/mongodb/mongo
142 lines
6.7 KiB
JavaScript
142 lines
6.7 KiB
JavaScript
/**
|
|
* Load this file when starting a mongo shell program in order to provide a callback to validate
|
|
* collections and indexes before shutting down a mongod while running JS tests.
|
|
*/
|
|
|
|
import {validateCollections} from "jstests/hooks/validate_collections.js";
|
|
import {CommandSequenceWithRetries} from "jstests/libs/command_sequence_with_retries.js";
|
|
|
|
MongoRunner.validateCollectionsCallback = function(port, options) {
|
|
options = options || {};
|
|
const CommandSequenceWithRetriesImpl =
|
|
options.CommandSequenceWithRetries || CommandSequenceWithRetries;
|
|
const validateCollectionsImpl = options.validateCollections || validateCollections;
|
|
|
|
if (jsTest.options().skipCollectionAndIndexValidation) {
|
|
print("Skipping collection validation during mongod shutdown");
|
|
return;
|
|
}
|
|
|
|
let conn;
|
|
try {
|
|
conn = new Mongo("localhost:" + port);
|
|
} catch (e) {
|
|
print("Skipping collection validation because we couldn't establish a connection to the" +
|
|
" server on port " + port);
|
|
return;
|
|
}
|
|
|
|
// Set secondaryOk=true so that we can run commands against any secondaries.
|
|
conn.setSecondaryOk();
|
|
|
|
let dbNames;
|
|
let result =
|
|
new CommandSequenceWithRetriesImpl(conn)
|
|
.then("running the isMaster command",
|
|
function(conn) {
|
|
const res = assert.commandWorked(conn.adminCommand({isMaster: 1}));
|
|
if (res.msg === "isdbgrid") {
|
|
return {shouldStop: true, reason: "not running validate against mongos"};
|
|
} else if (!res.ismaster && !res.secondary) {
|
|
return {
|
|
shouldStop: true,
|
|
reason: "not running validate since mongod isn't in the PRIMARY" +
|
|
" or SECONDARY states"
|
|
};
|
|
}
|
|
})
|
|
.then("authenticating",
|
|
function(conn) {
|
|
if (jsTest.options().keyFile) {
|
|
jsTest.authenticate(conn);
|
|
}
|
|
})
|
|
.then("best effort to step down node forever",
|
|
function(conn) {
|
|
if (conn.isReplicaSetMember()) {
|
|
// This node should never run for election again. If the node has not
|
|
// been initialized yet, then it cannot get elected.
|
|
const kFreezeTimeSecs = 24 * 60 * 60; // 24 hours.
|
|
|
|
assert.soon(
|
|
() => {
|
|
assert.commandWorkedOrFailedWithCode(
|
|
conn.adminCommand(
|
|
{replSetStepDown: kFreezeTimeSecs, force: true}),
|
|
[
|
|
ErrorCodes.NotWritablePrimary,
|
|
ErrorCodes.NotYetInitialized,
|
|
ErrorCodes.Unauthorized,
|
|
ErrorCodes.ConflictingOperationInProgress
|
|
]);
|
|
const res = conn.adminCommand({replSetFreeze: kFreezeTimeSecs});
|
|
assert.commandWorkedOrFailedWithCode(res, [
|
|
ErrorCodes.NotYetInitialized,
|
|
ErrorCodes.Unauthorized,
|
|
ErrorCodes.NotSecondary
|
|
]);
|
|
|
|
// If 'replSetFreeze' succeeds or fails with NotYetInitialized or
|
|
// Unauthorized, we do not need to retry the command because
|
|
// retrying will not work if the replica set is not yet
|
|
// initialized or if we are not authorized to run the command.
|
|
// This is why this is a "best-effort".
|
|
if (res.ok === 1 || res.code !== ErrorCodes.NotSecondary) {
|
|
return true;
|
|
}
|
|
|
|
// We only retry on NotSecondary error because 'replSetFreeze'
|
|
// could fail with NotSecondary if the node is currently primary
|
|
// or running for election. This could happen if there is a
|
|
// concurrent election running in parallel with the
|
|
// 'replSetStepDown' sent above.
|
|
jsTestLog(
|
|
"Retrying 'replSetStepDown' and 'replSetFreeze' in port " +
|
|
conn.port + " res: " + tojson(res));
|
|
return false;
|
|
},
|
|
"Timed out running 'replSetStepDown' and 'replSetFreeze' node in " +
|
|
"port " + conn.port);
|
|
}
|
|
})
|
|
.then("getting the list of databases",
|
|
function(conn) {
|
|
const res = conn.adminCommand({listDatabases: 1});
|
|
if (!res.ok) {
|
|
assert.commandFailedWithCode(res, ErrorCodes.Unauthorized);
|
|
return {shouldStop: true, reason: "cannot run listDatabases"};
|
|
}
|
|
assert.commandWorked(res);
|
|
dbNames = res.databases.map(dbInfo => dbInfo.name);
|
|
})
|
|
.execute();
|
|
|
|
if (!result.ok) {
|
|
print("Skipping collection validation: " + result.msg);
|
|
return;
|
|
}
|
|
|
|
const cmds = new CommandSequenceWithRetriesImpl(conn);
|
|
for (let i = 0; i < dbNames.length; ++i) {
|
|
const dbName = dbNames[i];
|
|
cmds.then("validating " + dbName, function(conn) {
|
|
const validateOptions = {full: true, enforceFastCount: true};
|
|
// TODO (SERVER-24266): Once fast counts are tolerant to unclean shutdowns, remove the
|
|
// check for TestData.allowUncleanShutdowns.
|
|
if (TestData.skipEnforceFastCountOnValidate || TestData.allowUncleanShutdowns) {
|
|
validateOptions.enforceFastCount = false;
|
|
}
|
|
|
|
const validate_res = validateCollectionsImpl(conn.getDB(dbName), validateOptions);
|
|
if (!validate_res.ok) {
|
|
return {
|
|
shouldStop: true,
|
|
reason: "collection validation failed " + tojson(validate_res)
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
assert.commandWorked(cmds.execute());
|
|
};
|