summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLingzhi Deng <lingzhi.deng@mongodb.com>2019-11-12 13:37:38 +0000
committerevergreen <evergreen@mongodb.com>2019-11-12 13:37:38 +0000
commit1f143f362f5b3fde180b8eb668c4e3f7933bd78d (patch)
tree84f75a6288e18757906a4e0611eb8cc9da9b7872 /src
parentb526ac445bb0b39b53bd291ef6d23a73a38a1f11 (diff)
downloadmongo-1f143f362f5b3fde180b8eb668c4e3f7933bd78d.tar.gz
SERVER-43941: Add "errorLabels" field to failCommand failpoint
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/commands.cpp17
-rw-r--r--src/mongo/db/commands.h1
-rw-r--r--src/mongo/db/error_labels.cpp18
-rw-r--r--src/mongo/db/error_labels.h5
-rw-r--r--src/mongo/db/service_entry_point_common.cpp16
-rw-r--r--src/mongo/s/commands/strategy.cpp9
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;
}