diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/commands.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 1 | ||||
-rw-r--r-- | src/mongo/db/error_labels.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/error_labels.h | 5 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 16 | ||||
-rw-r--r-- | src/mongo/s/commands/strategy.cpp | 9 |
7 files changed, 56 insertions, 11 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 3375d49d295..8c051a3916a 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1335,6 +1335,7 @@ env.Library( 'error_labels.cpp', ], LIBDEPS=[ + 'commands', 'logical_session_id', ], ) diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index fd6c28d145c..0345bd31680 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -50,6 +50,7 @@ #include "mongo/db/command_generic_argument.h" #include "mongo/db/commands/test_commands_enabled.h" #include "mongo/db/curop.h" +#include "mongo/db/error_labels.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/db/read_write_concern_defaults.h" @@ -471,6 +472,11 @@ constexpr StringData CommandHelpers::kHelpFieldName; MONGO_FAIL_POINT_DEFINE(failCommand); MONGO_FAIL_POINT_DEFINE(waitInCommandMarkKillOnClientDisconnect); +// A decoration representing error labels specified in a failCommand failpoint that has affected a +// command in this OperationContext. +const OperationContext::Decoration<boost::optional<BSONArray>> errorLabelsOverride = + OperationContext::declareDecoration<boost::optional<BSONArray>>(); + bool CommandHelpers::shouldActivateFailCommandFailPoint(const BSONObj& data, StringData cmdName, Client* client, @@ -515,7 +521,16 @@ void CommandHelpers::evaluateFailCommandFailPoint(OperationContext* opCtx, bool hasErrorCode; long long errorCode; failCommand.executeIf( - [&](const BSONObj&) { + [&](const BSONObj& data) { + if (data.hasField(kErrorLabelsFieldName) && + data[kErrorLabelsFieldName].type() == Array) { + // Propagate error labels specified in the failCommand failpoint to the + // OperationContext decoration to override getErrorLabels() behaviors. + invariant(!errorLabelsOverride(opCtx)); + errorLabelsOverride(opCtx).emplace( + data.getObjectField(kErrorLabelsFieldName).getOwned()); + } + if (closeConnection) { opCtx->getClient()->session()->end(); log() << "Failing command '" << commandName diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 6f174a40dfa..ea62ac51fc0 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -56,6 +56,7 @@ namespace mongo { extern FailPoint failCommand; extern FailPoint waitInCommandMarkKillOnClientDisconnect; +extern const OperationContext::Decoration<boost::optional<BSONArray>> errorLabelsOverride; class Command; class CommandInvocation; diff --git a/src/mongo/db/error_labels.cpp b/src/mongo/db/error_labels.cpp index d56ba6dd82d..383f4f77551 100644 --- a/src/mongo/db/error_labels.cpp +++ b/src/mongo/db/error_labels.cpp @@ -28,6 +28,7 @@ */ #include "mongo/db/error_labels.h" +#include "mongo/db/commands.h" namespace mongo { @@ -94,17 +95,28 @@ bool ErrorLabelBuilder::_isCommitOrAbort() const { _commandName == "abortTransaction"; } -BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions, +BSONObj getErrorLabels(OperationContext* opCtx, + const OperationSessionInfoFromClient& sessionOptions, const std::string& commandName, boost::optional<ErrorCodes::Error> code, boost::optional<ErrorCodes::Error> wcCode, bool isInternalClient) { - BSONArrayBuilder labelArray; + if (MONGO_unlikely(errorLabelsOverride(opCtx))) { + // This command was failed by a failCommand failpoint. Thus, we return the errorLabels + // specified in the failpoint to supress any other error labels that would otherwise be + // returned by the ErrorLabelBuilder. + if (errorLabelsOverride(opCtx).get().isEmpty()) { + return BSONObj(); + } else { + return BSON(kErrorLabelsFieldName << errorLabelsOverride(opCtx).get()); + } + } + BSONArrayBuilder labelArray; ErrorLabelBuilder labelBuilder(sessionOptions, commandName, code, wcCode, isInternalClient); labelBuilder.build(labelArray); - return (labelArray.arrSize() > 0) ? BSON("errorLabels" << labelArray.arr()) : BSONObj(); + return (labelArray.arrSize() > 0) ? BSON(kErrorLabelsFieldName << labelArray.arr()) : BSONObj(); } bool isTransientTransactionError(ErrorCodes::Error code, diff --git a/src/mongo/db/error_labels.h b/src/mongo/db/error_labels.h index 7490d862372..6066963fa98 100644 --- a/src/mongo/db/error_labels.h +++ b/src/mongo/db/error_labels.h @@ -32,7 +32,7 @@ #include "mongo/db/logical_session_id.h" namespace mongo { - +static constexpr StringData kErrorLabelsFieldName = "errorLabels"_sd; namespace ErrorLabel { // PLEASE CONSULT DRIVERS BEFORE ADDING NEW ERROR LABELS. static constexpr StringData kTransientTransaction = "TransientTransactionError"_sd; @@ -71,7 +71,8 @@ private: /** * Returns the error labels for the given error. */ -BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions, +BSONObj getErrorLabels(OperationContext* opCtx, + const OperationSessionInfoFromClient& sessionOptions, const std::string& commandName, boost::optional<ErrorCodes::Error> code, boost::optional<ErrorCodes::Error> wcCode, diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index c64241e5bff..4a42657c92a 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -628,6 +628,14 @@ bool runCommandImpl(OperationContext* opCtx, [&](const BSONObj& data) { bb.append(data["writeConcernError"]); reallyWait = false; + if (data.hasField(kErrorLabelsFieldName) && + data[kErrorLabelsFieldName].type() == Array) { + // Propagate error labels specified in the failCommand failpoint to the + // OperationContext decoration to override getErrorLabels() behaviors. + invariant(!errorLabelsOverride(opCtx)); + errorLabelsOverride(opCtx).emplace( + data.getObjectField(kErrorLabelsFieldName).getOwned()); + } }, [&](const BSONObj& data) { return CommandHelpers::shouldActivateFailCommandFailPoint( @@ -719,8 +727,8 @@ bool runCommandImpl(OperationContext* opCtx, } auto isInternalClient = opCtx->getClient()->session() && (opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient); - auto errorLabels = - getErrorLabels(sessionOptions, command->getName(), code, wcCode, isInternalClient); + auto errorLabels = getErrorLabels( + opCtx, sessionOptions, command->getName(), code, wcCode, isInternalClient); replyBuilder->getBodyBuilder().appendElements(errorLabels); } @@ -1024,8 +1032,8 @@ void execCommandDatabase(OperationContext* opCtx, } auto isInternalClient = opCtx->getClient()->session() && (opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient); - auto errorLabels = - getErrorLabels(sessionOptions, command->getName(), e.code(), wcCode, isInternalClient); + auto errorLabels = getErrorLabels( + opCtx, sessionOptions, command->getName(), e.code(), wcCode, isInternalClient); extraFieldsBuilder.appendElements(errorLabels); BSONObjBuilder metadataBob; diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index 6a4e5f6da85..2f734c11654 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -274,6 +274,13 @@ void execCommandClient(OperationContext* opCtx, failCommand.executeIf( [&](const BSONObj& data) { result->getBodyBuilder().append(data["writeConcernError"]); + if (data.hasField(kErrorLabelsFieldName) && + data[kErrorLabelsFieldName].type() == Array) { + auto labels = data.getObjectField(kErrorLabelsFieldName).getOwned(); + if (!labels.isEmpty()) { + result->getBodyBuilder().append(kErrorLabelsFieldName, BSONArray(labels)); + } + } }, [&](const BSONObj& data) { return CommandHelpers::shouldActivateFailCommandFailPoint( @@ -629,7 +636,7 @@ void runCommand(OperationContext* opCtx, // isInternalClient is set to true to suppress mongos from returning the RetryableWriteError // label. auto errorLabels = getErrorLabels( - osi, command->getName(), e.code(), boost::none, true /* isInternalClient */); + opCtx, osi, command->getName(), e.code(), boost::none, true /* isInternalClient */); errorBuilder->appendElements(errorLabels); throw; } |