From 6894f176cac940406c9de66b3d091c2e6044afc3 Mon Sep 17 00:00:00 2001 From: Jack Mulrow Date: Mon, 26 Aug 2019 22:49:27 +0000 Subject: SERVER-43011 Add optional namespace restriction to failCommand failpoint (cherry picked from commit f7cd49a1930516cea21437bd7a32e7f8bbf0a006) --- jstests/core/failcommand_failpoint.js | 43 +++++++++++++++++++++++++++++ src/mongo/db/commands.cpp | 13 +++++++-- src/mongo/db/commands.h | 7 +++-- src/mongo/db/service_entry_point_common.cpp | 4 +-- src/mongo/s/commands/strategy.cpp | 4 +-- 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/jstests/core/failcommand_failpoint.js b/jstests/core/failcommand_failpoint.js index e78d39e3d50..4e94e052f8a 100644 --- a/jstests/core/failcommand_failpoint.js +++ b/jstests/core/failcommand_failpoint.js @@ -285,4 +285,47 @@ res = testDB.runCommand({insert: "test", documents: [{a: "something else"}]}); assert.commandWorkedIgnoringWriteConcernErrors(res); assert.eq(res.writeConcernError, {code: 12345, errmsg: "hello"}); assert.commandWorked(testDB.runCommand({insert: "test", documents: [{b: "or_other"}]})); + +// +// Test that the namespace parameter is obeyed. +// +assert.commandWorked(adminDB.runCommand({ + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + errorCode: ErrorCodes.InternalError, + failCommands: ["find"], + namespace: testDB.getName() + ".foo", + threadName: threadName, + } +})); + +// A find against a different namespace should not trigger the failpoint. +assert.commandWorked(testDB.runCommand({find: "test"})); + +// A find against the namespace given to the failpoint should trigger the failpoint. +assert.commandFailedWithCode(testDB.runCommand({find: "foo"}), ErrorCodes.InternalError); + +// +// Test that the namespace parameter is obeyed for write concern errors. +// +assert.commandWorked(adminDB.runCommand({ + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + namespace: testDB.getName() + ".foo", + threadName: threadName, + writeConcernError: {code: ErrorCodes.InternalError, errmsg: "foo"}, + } +})); + +// An insert to a different namespace should not trigger the failpoint. +assert.commandWorked( + testDB.runCommand({insert: "test", documents: [{x: "doc_for_namespace_no_wce"}]})); + +// An insert to the namespace given to the failpoint should trigger the failpoint. +res = assert.commandWorkedIgnoringWriteConcernErrors(testDB.runCommand( + {insert: "foo", documents: [{x: "doc_for_namespace_case_should_trigger_wce"}]})); +assert.eq(res.writeConcernError, {code: ErrorCodes.InternalError, errmsg: "foo"}); }()); diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 89ae334d63b..fc356126564 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -460,7 +460,8 @@ MONGO_FAIL_POINT_DEFINE(waitInCommandMarkKillOnClientDisconnect); bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data, StringData cmdName, - Client* client) { + Client* client, + const NamespaceString& nss) { if (cmdName == "configureFailPoint"_sd) // Banned even if in failCommands. return false; @@ -481,6 +482,10 @@ bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data, return false; } + if (data.hasField("namespace") && nss != NamespaceString(data.getStringField("namespace"))) { + return false; + } + for (auto&& failCommand : data.getObjectField("failCommands")) { if (failCommand.type() == String && failCommand.valueStringData() == cmdName) { return true; @@ -490,7 +495,9 @@ bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data, return false; } -void CommandHelpers::evaluateFailCommandFailPoint(OperationContext* opCtx, StringData commandName) { +void CommandHelpers::evaluateFailCommandFailPoint(OperationContext* opCtx, + StringData commandName, + const NamespaceString& nss) { bool closeConnection, hasErrorCode; long long errorCode; @@ -501,7 +508,7 @@ void CommandHelpers::evaluateFailCommandFailPoint(OperationContext* opCtx, Strin hasErrorCode = data.hasField("errorCode") && bsonExtractIntegerField(data, "errorCode", &errorCode).isOK(); - return shouldActivateFailCommandFailPoint(data, commandName, opCtx->getClient()) && + return shouldActivateFailCommandFailPoint(data, commandName, opCtx->getClient(), nss) && (closeConnection || hasErrorCode); }) { if (closeConnection) { diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 0715354ba07..09a02bbeb28 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -222,12 +222,15 @@ struct CommandHelpers { */ static bool shouldActivateFailCommandFailPoint(const BSONObj& data, StringData cmdName, - Client* client); + Client* client, + const NamespaceString& nss); /** * Possibly uasserts according to the "failCommand" fail point. */ - static void evaluateFailCommandFailPoint(OperationContext* opCtx, StringData commandName); + static void evaluateFailCommandFailPoint(OperationContext* opCtx, + StringData commandName, + const NamespaceString& nss); /** * Handles marking kill on client disconnect. diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 4b0216aac4a..82cf2169c7f 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -571,7 +571,7 @@ bool runCommandImpl(OperationContext* opCtx, auto waitForWriteConcern = [&](auto&& bb) { MONGO_FAIL_POINT_BLOCK_IF(failCommand, data, [&](const BSONObj& data) { return CommandHelpers::shouldActivateFailCommandFailPoint( - data, request.getCommandName(), opCtx->getClient()) && + data, request.getCommandName(), opCtx->getClient(), invocation->ns()) && data.hasField("writeConcernError"); }) { bb.append(data.getData()["writeConcernError"]); @@ -716,7 +716,7 @@ void execCommandDatabase(OperationContext* opCtx, replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet, opCtx->getServiceContext()->getStorageEngine()->supportsDocLocking()); - CommandHelpers::evaluateFailCommandFailPoint(opCtx, command->getName()); + CommandHelpers::evaluateFailCommandFailPoint(opCtx, command->getName(), invocation->ns()); const auto dbname = request.getDatabase().toString(); uassert( diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index 9228f0c0ef3..76e2d0d2fc9 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -300,7 +300,7 @@ void execCommandClient(OperationContext* opCtx, MONGO_FAIL_POINT_BLOCK_IF(failCommand, data, [&](const BSONObj& data) { return CommandHelpers::shouldActivateFailCommandFailPoint( - data, request.getCommandName(), opCtx->getClient()) && + data, request.getCommandName(), opCtx->getClient(), invocation->ns()) && data.hasField("writeConcernError"); }) { body.append(data.getData()["writeConcernError"]); @@ -407,7 +407,7 @@ void runCommand(OperationContext* opCtx, boost::optional routerSession; try { - CommandHelpers::evaluateFailCommandFailPoint(opCtx, commandName); + CommandHelpers::evaluateFailCommandFailPoint(opCtx, commandName, invocation->ns()); if (osi.getAutocommit()) { routerSession.emplace(opCtx); -- cgit v1.2.1