mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-30 17:10:48 +01:00
184 lines
6.5 KiB
JavaScript
184 lines
6.5 KiB
JavaScript
/**
|
|
* Runs the validate command with {background:true} against all nodes (replica set members and
|
|
* standalone nodes, not sharded clusters) concurrently with running tests.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
(function() {
|
|
load('jstests/libs/discover_topology.js'); // For Topology and DiscoverTopology.
|
|
load('jstests/libs/parallelTester.js'); // For Thread.
|
|
|
|
if (typeof db === 'undefined') {
|
|
throw new Error(
|
|
"Expected mongo shell to be connected a server, but global 'db' object isn't defined");
|
|
}
|
|
|
|
// Disable implicit sessions so FSM workloads that kill random sessions won't interrupt the
|
|
// operations in this test that aren't resilient to interruptions.
|
|
TestData.disableImplicitSessions = true;
|
|
|
|
const conn = db.getMongo();
|
|
const topology = DiscoverTopology.findConnectedNodes(conn);
|
|
|
|
/**
|
|
* Returns true if the error code is transient and does not indicate data corruption.
|
|
*/
|
|
const isIgnorableError = function ignorableError(codeName) {
|
|
if (codeName == "NamespaceNotFound" || codeName == "Interrupted" ||
|
|
codeName == "CommandNotSupportedOnView" || codeName == "InterruptedAtShutdown" ||
|
|
codeName == "InvalidViewDefinition") {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Runs validate commands with {background:true} against 'host' for all collections it possesses.
|
|
*
|
|
* Returns the cumulative command failure results, if there are any, in an object
|
|
* { ok: 0, error: [{cmd-res}, {cmd-res}, ... ]}
|
|
* Or simply OK if all cmds were successful.
|
|
* {ok: 1}
|
|
*
|
|
* This function should not throw if everything is working properly.
|
|
*/
|
|
const validateCollectionsBackgroundThread = function validateCollectionsBackground(
|
|
host, isIgnorableErrorFunc) {
|
|
// Calls 'func' with the print() function overridden to be a no-op.
|
|
const quietly = (func) => {
|
|
const printOriginal = print;
|
|
try {
|
|
print = Function.prototype;
|
|
func();
|
|
} finally {
|
|
print = printOriginal;
|
|
}
|
|
};
|
|
|
|
// Suppress the log messages generated establishing new mongo connections. The
|
|
// run_validate_collections_background.js hook is executed frequently by resmoke.py and
|
|
// could lead to generating an overwhelming amount of log messages.
|
|
let conn;
|
|
quietly(() => {
|
|
conn = new Mongo(host);
|
|
});
|
|
assert.neq(null,
|
|
conn,
|
|
"Failed to connect to host '" + host + "' for background collection validation");
|
|
|
|
if (!conn.adminCommand("serverStatus").storageEngine.supportsCheckpointCursors) {
|
|
print("Skipping background validation against test node: " + host +
|
|
" because its storage engine does not support background validation (checkpoints).");
|
|
return {ok: 1};
|
|
}
|
|
|
|
// Filter out arbiters.
|
|
if (conn.adminCommand({isMaster: 1}).arbiterOnly) {
|
|
print("Skipping background validation against test node: " + host +
|
|
" because it is an arbiter and has no data.");
|
|
return {ok: 1};
|
|
}
|
|
|
|
print("Running background validation on all collections on test node: " + host);
|
|
|
|
// Save a map of namespace to validate cmd results for any cmds that fail so that we can return
|
|
// the results afterwards.
|
|
let failedValidateResults = [];
|
|
|
|
// Validate all collections in every database.
|
|
|
|
const dbNames =
|
|
assert
|
|
.commandWorked(conn.adminCommand(
|
|
{"listDatabases": 1, "nameOnly": true, "$readPreference": {"mode": "nearest"}}))
|
|
.databases.map(function(z) {
|
|
return z.name;
|
|
});
|
|
|
|
for (let dbName of dbNames) {
|
|
let db = conn.getDB(dbName);
|
|
|
|
const listCollRes = assert.commandWorked(db.runCommand({
|
|
"listCollections": 1,
|
|
"nameOnly": true,
|
|
"filter": {$or: [{type: 'collection'}, {type: {$exists: false}}]},
|
|
"$readPreference": {"mode": "nearest"},
|
|
}));
|
|
const collectionNames = new DBCommandCursor(db, listCollRes).map(function(z) {
|
|
return z.name;
|
|
});
|
|
|
|
for (let collectionName of collectionNames) {
|
|
let res = conn.getDB(dbName).getCollection(collectionName).runCommand({
|
|
"validate": collectionName,
|
|
background: true,
|
|
"$readPreference": {"mode": "nearest"}
|
|
});
|
|
|
|
if ((!res.ok && !isIgnorableErrorFunc(res.codeName)) || (res.valid === false)) {
|
|
failedValidateResults.push({"ns": dbName + "." + collectionName, "res": res});
|
|
}
|
|
}
|
|
}
|
|
|
|
// If any commands failed, format and return an error.
|
|
if (failedValidateResults.length) {
|
|
let errorsArray = [];
|
|
for (let nsAndRes of failedValidateResults) {
|
|
errorsArray.push({"namespace": nsAndRes.ns, "res": nsAndRes.res});
|
|
}
|
|
|
|
const heading = "Validate command(s) with {background:true} failed against mongod";
|
|
print(heading + " '" + conn.host + "': \n" + tojson(errorsArray));
|
|
|
|
return {ok: 0, error: "Validate failure (search for the following heading): " + heading};
|
|
}
|
|
|
|
return {ok: 1};
|
|
};
|
|
|
|
if (topology.type === Topology.kStandalone) {
|
|
let res = validateCollectionsBackgroundThread(topology.mongod);
|
|
assert.commandWorked(
|
|
res,
|
|
() => 'background collection validation against the standalone failed: ' + tojson(res));
|
|
} else if (topology.type === Topology.kReplicaSet) {
|
|
const threads = [];
|
|
try {
|
|
for (let replicaMember of topology.nodes) {
|
|
const thread =
|
|
new Thread(validateCollectionsBackgroundThread, replicaMember, isIgnorableError);
|
|
threads.push(thread);
|
|
thread.start();
|
|
}
|
|
} finally {
|
|
// Wait for each thread to finish and gather any errors.
|
|
let gatheredErrors = [];
|
|
const returnData = threads.map(thread => {
|
|
try {
|
|
thread.join();
|
|
|
|
// Calling returnData can cause an error thrown in the thread to be thrown again, so
|
|
// we do this in a try-catch block.
|
|
let res = thread.returnData();
|
|
|
|
if (!res.ok) {
|
|
gatheredErrors.push(res);
|
|
}
|
|
} catch (e) {
|
|
gatheredErrors.push(e);
|
|
}
|
|
});
|
|
|
|
if (gatheredErrors.length) {
|
|
throw new Error(
|
|
"Background collection validation was not successful against all replica set " +
|
|
"members: \n" + tojson(gatheredErrors));
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error('Unsupported topology configuration: ' + tojson(topology));
|
|
}
|
|
})();
|