mirror of https://github.com/mongodb/mongo
269 lines
9.6 KiB
JavaScript
269 lines
9.6 KiB
JavaScript
/**
|
|
* Test BSON validation in the dbCheck command.
|
|
*
|
|
* @tags: [
|
|
* requires_fcv_80,
|
|
* corrupts_data,
|
|
* ]
|
|
*/
|
|
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {
|
|
checkHealthLog,
|
|
clearHealthLog,
|
|
forEachNonArbiterNode,
|
|
resetAndInsert,
|
|
runDbCheck,
|
|
} from "jstests/replsets/libs/dbcheck_utils.js";
|
|
|
|
// Skipping data consistency checks because invalid BSON is inserted into primary and secondary
|
|
// separately.
|
|
TestData.skipCollectionAndIndexValidation = true;
|
|
TestData.skipCheckDBHashes = true;
|
|
|
|
const dbName = "dbCheckBSONValidation";
|
|
const collName = "dbCheckBSONValidation-collection";
|
|
|
|
const replSet = new ReplSetTest({
|
|
name: jsTestName(),
|
|
nodes: 2,
|
|
nodeOptions: {setParameter: {dbCheckHealthLogEveryNBatches: 1}},
|
|
});
|
|
replSet.startSet();
|
|
replSet.initiate();
|
|
|
|
const primary = replSet.getPrimary();
|
|
const primaryDb = primary.getDB(dbName);
|
|
const secondary = replSet.getSecondary();
|
|
const secondaryDb = secondary.getDB(dbName);
|
|
const nDocs = 10;
|
|
const maxDocsPerBatch = 2;
|
|
|
|
const errQuery = {
|
|
operation: "dbCheckBatch",
|
|
severity: "error",
|
|
};
|
|
const invalidBSONQuery = {
|
|
operation: "dbCheckBatch",
|
|
severity: "error",
|
|
msg: "Document is not well-formed BSON",
|
|
"data.context.recordID": {$exists: true},
|
|
};
|
|
const invalidHashQuery = {
|
|
operation: "dbCheckBatch",
|
|
severity: "error",
|
|
msg: "dbCheck batch inconsistent",
|
|
"data.md5": {$exists: true},
|
|
};
|
|
const warningQuery = {
|
|
operation: "dbCheckBatch",
|
|
severity: "warning",
|
|
};
|
|
const BSONWarningQuery = {
|
|
operation: "dbCheckBatch",
|
|
severity: "warning",
|
|
msg: "Document is not well-formed BSON",
|
|
};
|
|
const successfulBatchQuery = {
|
|
operation: "dbCheckBatch",
|
|
severity: "info",
|
|
msg: "dbCheck batch consistent",
|
|
"data.count": maxDocsPerBatch,
|
|
};
|
|
const errAndWarningQuery = {
|
|
operation: "dbCheckBatch",
|
|
$or: [{severity: "error"}, {severity: "warning"}],
|
|
};
|
|
|
|
const doc1 = {
|
|
_id: 0,
|
|
a: 1,
|
|
};
|
|
|
|
// The default WC is majority and godinsert command on a secondary is incompatible with
|
|
// wc:majority.
|
|
assert.commandWorked(
|
|
primary.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}),
|
|
);
|
|
|
|
// Insert corrupt document for testing via failpoint.
|
|
const insertCorruptDocument = function (db, collName) {
|
|
assert.commandWorked(db.adminCommand({configureFailPoint: "corruptDocumentOnInsert", mode: "alwaysOn"}));
|
|
// Use godinsert to insert into the node directly.
|
|
assert.commandWorked(db.runCommand({godinsert: collName, obj: doc1}));
|
|
assert.commandWorked(db.adminCommand({configureFailPoint: "corruptDocumentOnInsert", mode: "off"}));
|
|
};
|
|
|
|
function testKDefaultBSONValidation() {
|
|
jsTestLog("Testing that dbCheck logs error in health log when node has kDefault invalid BSON.");
|
|
|
|
clearHealthLog(replSet);
|
|
|
|
// Insert invalid BSON that fails the kDefault check into both nodes in the replica set.
|
|
assert.commandWorked(primaryDb.createCollection(collName));
|
|
replSet.awaitReplication();
|
|
forEachNonArbiterNode(replSet, function (node) {
|
|
insertCorruptDocument(node.getDB(dbName), collName);
|
|
});
|
|
|
|
// Both primary and secondary should have error in health log for invalid BSON.
|
|
runDbCheck(
|
|
replSet,
|
|
primaryDb,
|
|
collName,
|
|
{validateMode: "dataConsistencyAndMissingIndexKeysCheck"},
|
|
true /* awaitCompletion */,
|
|
);
|
|
|
|
// Primary and secondary have the same invalid BSON document so there is an error for invalid
|
|
// BSON but not data inconsistency. We check that the only errors in the health log are for
|
|
// invalid BSON.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, invalidBSONQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, invalidBSONQuery, 1);
|
|
checkHealthLog(primary.getDB("local").system.healthlog, errQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, errQuery, 1);
|
|
}
|
|
|
|
function testPrimaryInvalidBson() {
|
|
jsTestLog("Testing when the primary has invalid BSON but the secondary does not.");
|
|
|
|
clearHealthLog(replSet);
|
|
|
|
primaryDb[collName].drop();
|
|
assert.commandWorked(primaryDb.createCollection(collName));
|
|
replSet.awaitReplication();
|
|
|
|
// Insert an invalid document on the primary.
|
|
insertCorruptDocument(primaryDb, collName);
|
|
|
|
// Insert a normal document on the secondary.
|
|
assert.commandWorked(secondaryDb.runCommand({godinsert: collName, obj: doc1}));
|
|
|
|
runDbCheck(
|
|
replSet,
|
|
primaryDb,
|
|
collName,
|
|
{maxDocsPerBatch: maxDocsPerBatch, validateMode: "dataConsistencyAndMissingIndexKeysCheck"},
|
|
true /* awaitCompletion */,
|
|
);
|
|
|
|
// Verify primary logs an error for invalid BSON while the secondary logs an error for data
|
|
// inconsistency.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, invalidBSONQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, invalidHashQuery, 1);
|
|
|
|
// Verify that the primary and secondary do not have other error/warning logs.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, errAndWarningQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, errAndWarningQuery, 1);
|
|
}
|
|
|
|
function testSecondaryInvalidBson() {
|
|
jsTestLog("Testing when the secondary has invalid BSON but the primary does not.");
|
|
|
|
clearHealthLog(replSet);
|
|
|
|
primaryDb[collName].drop();
|
|
assert.commandWorked(primaryDb.createCollection(collName));
|
|
replSet.awaitReplication();
|
|
|
|
// Insert a normal document on the primary.
|
|
assert.commandWorked(primaryDb.runCommand({godinsert: collName, obj: doc1}));
|
|
|
|
// Insert an invalid document on the secondary.
|
|
insertCorruptDocument(secondaryDb, collName);
|
|
|
|
runDbCheck(
|
|
replSet,
|
|
primaryDb,
|
|
collName,
|
|
{maxDocsPerBatch: maxDocsPerBatch, validateMode: "dataConsistencyAndMissingIndexKeysCheck"},
|
|
true /* awaitCompletion */,
|
|
);
|
|
|
|
// Verify secondary logs an error for invalid BSON and data inconsistency.
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, invalidBSONQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, invalidHashQuery, 1);
|
|
|
|
// Verify that the primary and secondary do not have other error/warning logs.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, errAndWarningQuery, 0);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, errAndWarningQuery, 2);
|
|
}
|
|
|
|
function testMultipleBatches() {
|
|
jsTestLog("Testing that invalid BSON check works across multiple batches.");
|
|
|
|
clearHealthLog(replSet);
|
|
|
|
// Primary contains 10 valid documents, secondary contains 9 valid and 1 invalid document.
|
|
resetAndInsert(replSet, primaryDb, collName, nDocs - 1);
|
|
assert.commandWorked(primaryDb.runCommand({godinsert: collName, obj: {_id: 0, a: nDocs}}));
|
|
insertCorruptDocument(secondaryDb, collName);
|
|
|
|
runDbCheck(
|
|
replSet,
|
|
primaryDb,
|
|
collName,
|
|
{maxDocsPerBatch: maxDocsPerBatch, validateMode: "dataConsistencyAndMissingIndexKeysCheck"},
|
|
true /* awaitCompletion */,
|
|
);
|
|
|
|
// Secondary logs an error for invalid BSON and data inconsistency.
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, invalidBSONQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, invalidHashQuery, 1);
|
|
|
|
// Primary and secondary do not terminate after first batch and finish dbCheck successfully.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, successfulBatchQuery, nDocs / maxDocsPerBatch);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, successfulBatchQuery, nDocs / maxDocsPerBatch - 1);
|
|
// There should be no other error queries.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, errQuery, 0);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, errQuery, 2);
|
|
checkHealthLog(primary.getDB("local").system.healthlog, {operation: "dbCheckStop"}, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, {operation: "dbCheckStop"}, 1);
|
|
}
|
|
|
|
function testInvalidUuid() {
|
|
jsTestLog(
|
|
"Testing that a BSON document that is structurally valid but invalid in other ways (such as by having UUID that have incorrect lengths) is included in hashing but logs a warning",
|
|
);
|
|
clearHealthLog(replSet);
|
|
|
|
primaryDb[collName].drop();
|
|
assert.commandWorked(primaryDb.createCollection(collName));
|
|
replSet.awaitReplication();
|
|
|
|
// Insert 2 documents with invalid UUID (length is 4 or 20 instead of 16).
|
|
assert.commandWorked(primaryDb[collName].insert({u: HexData(4, "deadbeef")}));
|
|
assert.commandWorked(primaryDb[collName].insert({u: HexData(4, "deadbeef".repeat(5))}));
|
|
replSet.awaitReplication();
|
|
|
|
runDbCheck(
|
|
replSet,
|
|
primaryDb,
|
|
collName,
|
|
{
|
|
maxDocsPerBatch: maxDocsPerBatch,
|
|
validateMode: "dataConsistencyAndMissingIndexKeysCheck",
|
|
bsonValidateMode: "kFull",
|
|
},
|
|
true /* awaitCompletion */,
|
|
);
|
|
|
|
// Verify both primary and secondary log a warning for invalid BSON (k).
|
|
checkHealthLog(primary.getDB("local").system.healthlog, BSONWarningQuery, 2);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, BSONWarningQuery, 2);
|
|
|
|
// Verify that the primary and secondary do not have other error/warning logs.
|
|
checkHealthLog(primary.getDB("local").system.healthlog, successfulBatchQuery, 1);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, successfulBatchQuery, 1);
|
|
checkHealthLog(primary.getDB("local").system.healthlog, errAndWarningQuery, 2);
|
|
checkHealthLog(secondary.getDB("local").system.healthlog, errAndWarningQuery, 2);
|
|
}
|
|
|
|
testKDefaultBSONValidation();
|
|
testPrimaryInvalidBson();
|
|
testSecondaryInvalidBson();
|
|
testMultipleBatches();
|
|
testInvalidUuid();
|
|
|
|
replSet.stopSet();
|