From 0ff5e5e7cc09c31d3fe260cf6602f461e6e20bb6 Mon Sep 17 00:00:00 2001 From: Lingzhi Deng Date: Wed, 6 Nov 2019 15:18:44 +0000 Subject: SERVER-41245: Add RetryableWriteError error label --- src/mongo/db/SConscript | 13 +- src/mongo/db/error_labels.cpp | 139 ++++++++++++++++++ src/mongo/db/error_labels.h | 87 ++++++++++++ src/mongo/db/error_labels_test.cpp | 209 ++++++++++++++++++++++++++++ src/mongo/db/handle_request_response.cpp | 61 -------- src/mongo/db/handle_request_response.h | 44 ------ src/mongo/db/ops/write_ops_exec.cpp | 4 +- src/mongo/db/service_entry_point_common.cpp | 32 +++-- 8 files changed, 470 insertions(+), 119 deletions(-) create mode 100644 src/mongo/db/error_labels.cpp create mode 100644 src/mongo/db/error_labels.h create mode 100644 src/mongo/db/error_labels_test.cpp delete mode 100644 src/mongo/db/handle_request_response.cpp delete mode 100644 src/mongo/db/handle_request_response.h (limited to 'src/mongo/db') diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 8305d3dc094..3375d49d295 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1329,13 +1329,23 @@ env.Library( ] ) +env.Library( + target='error_labels', + source=[ + 'error_labels.cpp', + ], + LIBDEPS=[ + 'logical_session_id', + ], +) + env.Library( target='shared_request_handling', source=[ - 'handle_request_response.cpp', 'transaction_validation.cpp', ], LIBDEPS=[ + 'error_labels', 'logical_session_cache_impl', ], ) @@ -1794,6 +1804,7 @@ envWithAsio.CppUnitTest( 'transaction_participant_test.cpp', 'update_index_data_test.cpp', 'write_concern_options_test.cpp', + 'error_labels_test.cpp', env.Idlc('commands_test_example.idl')[0], ], LIBDEPS=[ diff --git a/src/mongo/db/error_labels.cpp b/src/mongo/db/error_labels.cpp new file mode 100644 index 00000000000..d56ba6dd82d --- /dev/null +++ b/src/mongo/db/error_labels.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/error_labels.h" + +namespace mongo { + +bool ErrorLabelBuilder::isTransientTransactionError() const { + // Note that we only apply the TransientTransactionError label if the "autocommit" field is + // present in the session options. When present, "autocommit" will always be false, so we + // don't check its value. There is no point in returning TransientTransactionError label if + // we have already tried to abort it. An error code for which isTransientTransactionError() + // is true indicates a transaction failure with no persistent side effects. + return _code && _sessionOptions.getTxnNumber() && _sessionOptions.getAutocommit() && + mongo::isTransientTransactionError(_code.get(), _wcCode != boost::none, _isCommitOrAbort()); +} + +bool ErrorLabelBuilder::isRetryableWriteError() const { + // Do not return RetryableWriteError labels to internal clients (e.g. mongos). + if (_isInternalClient) { + return false; + } + + auto isRetryableWrite = [&]() { + return _sessionOptions.getTxnNumber() && !_sessionOptions.getAutocommit(); + }; + + auto isTransactionCommitOrAbort = [&]() { + return _sessionOptions.getTxnNumber() && _sessionOptions.getAutocommit() && + _isCommitOrAbort(); + }; + + // Return with RetryableWriteError label on retryable error codes for retryable writes or + // transactions commit/abort. + if (isRetryableWrite() || isTransactionCommitOrAbort()) { + if ((_code && ErrorCodes::isRetriableError(_code.get())) || + (_wcCode && ErrorCodes::isRetriableError(_wcCode.get()))) { + return true; + } + } + return false; +} + +bool ErrorLabelBuilder::isNonResumableChangeStreamError() const { + return _code && ErrorCodes::isNonResumableChangeStreamError(_code.get()); +} + +void ErrorLabelBuilder::build(BSONArrayBuilder& labels) const { + // PLEASE CONSULT DRIVERS BEFORE ADDING NEW ERROR LABELS. + bool hasTransientTransactionError = false; + if (isTransientTransactionError()) { + labels << ErrorLabel::kTransientTransaction; + hasTransientTransactionError = true; + } + if (isRetryableWriteError()) { + // RetryableWriteError and TransientTransactionError are mutually exclusive. + invariant(!hasTransientTransactionError); + labels << ErrorLabel::kRetryableWrite; + } + if (isNonResumableChangeStreamError()) { + labels << ErrorLabel::kNonResumableChangeStream; + } + return; +} + +bool ErrorLabelBuilder::_isCommitOrAbort() const { + return _commandName == "commitTransaction" || _commandName == "coordinateCommitTransaction" || + _commandName == "abortTransaction"; +} + +BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions, + const std::string& commandName, + boost::optional code, + boost::optional wcCode, + bool isInternalClient) { + BSONArrayBuilder labelArray; + + ErrorLabelBuilder labelBuilder(sessionOptions, commandName, code, wcCode, isInternalClient); + labelBuilder.build(labelArray); + + return (labelArray.arrSize() > 0) ? BSON("errorLabels" << labelArray.arr()) : BSONObj(); +} + +bool isTransientTransactionError(ErrorCodes::Error code, + bool hasWriteConcernError, + bool isCommitOrAbort) { + bool isTransient; + switch (code) { + case ErrorCodes::WriteConflict: + case ErrorCodes::LockTimeout: + case ErrorCodes::PreparedTransactionInProgress: + isTransient = true; + break; + default: + isTransient = false; + break; + } + + isTransient |= ErrorCodes::isSnapshotError(code) || ErrorCodes::isNeedRetargettingError(code) || + code == ErrorCodes::StaleDbVersion; + + if (isCommitOrAbort) { + // On NoSuchTransaction it's safe to retry the whole transaction only if the data cannot be + // rolled back. + isTransient |= code == ErrorCodes::NoSuchTransaction && !hasWriteConcernError; + } else { + isTransient |= ErrorCodes::isRetriableError(code) || code == ErrorCodes::NoSuchTransaction; + } + + return isTransient; +} + +} // namespace mongo diff --git a/src/mongo/db/error_labels.h b/src/mongo/db/error_labels.h new file mode 100644 index 00000000000..7490d862372 --- /dev/null +++ b/src/mongo/db/error_labels.h @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/db/logical_session_id.h" + +namespace mongo { + +namespace ErrorLabel { +// PLEASE CONSULT DRIVERS BEFORE ADDING NEW ERROR LABELS. +static constexpr StringData kTransientTransaction = "TransientTransactionError"_sd; +static constexpr StringData kRetryableWrite = "RetryableWriteError"_sd; +static constexpr StringData kNonResumableChangeStream = "NonResumableChangeStreamError"_sd; +} // namespace ErrorLabel + +class ErrorLabelBuilder { +public: + ErrorLabelBuilder(const OperationSessionInfoFromClient& sessionOptions, + const std::string& commandName, + boost::optional code, + boost::optional wcCode, + bool isInternalClient) + : _sessionOptions(sessionOptions), + _commandName(commandName), + _code(code), + _wcCode(wcCode), + _isInternalClient(isInternalClient) {} + + void build(BSONArrayBuilder& labels) const; + + bool isTransientTransactionError() const; + bool isRetryableWriteError() const; + bool isNonResumableChangeStreamError() const; + +private: + bool _isCommitOrAbort() const; + const OperationSessionInfoFromClient& _sessionOptions; + const std::string& _commandName; + boost::optional _code; + boost::optional _wcCode; + bool _isInternalClient; +}; + +/** + * Returns the error labels for the given error. + */ +BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions, + const std::string& commandName, + boost::optional code, + boost::optional wcCode, + bool isInternalClient); + +/** + * Whether a write error in a transaction should be labelled with "TransientTransactionError". + */ +bool isTransientTransactionError(ErrorCodes::Error code, + bool hasWriteConcernError, + bool isCommitOrAbort); + +} // namespace mongo diff --git a/src/mongo/db/error_labels_test.cpp b/src/mongo/db/error_labels_test.cpp new file mode 100644 index 00000000000..92d6d4b73a2 --- /dev/null +++ b/src/mongo/db/error_labels_test.cpp @@ -0,0 +1,209 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/error_labels.h" +#include "mongo/db/logical_session_id.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +TEST(IsTransientTransactionErrorTest, NetworkErrorsAreTransientBeforeCommit) { + ASSERT_TRUE(isTransientTransactionError(ErrorCodes::HostUnreachable, + false /* hasWriteConcernError */, + false /* isCommitOrAbort */)); +} + +TEST(IsTransientTransactionErrorTest, NetworkErrorsAreNotTransientOnCommit) { + ASSERT_FALSE(isTransientTransactionError( + ErrorCodes::HostUnreachable, false /* hasWriteConcernError */, true /* isCommitOrAbort */)); +} + +TEST(IsTransientTransactionErrorTest, RetryableWriteErrorsAreNotTransientOnAbort) { + ASSERT_FALSE(isTransientTransactionError( + ErrorCodes::NotMaster, false /* hasWriteConcernError */, true /* isCommitOrAbort */)); +} + +TEST(IsTransientTransactionErrorTest, + NoSuchTransactionWithWriteConcernErrorsAreNotTransientOnCommit) { + ASSERT_FALSE(isTransientTransactionError(ErrorCodes::NoSuchTransaction, + true /* hasWriteConcernError */, + true /* isCommitOrAbort */)); +} + +TEST(IsTransientTransactionErrorTest, + NoSuchTransactionWithoutWriteConcernErrorsAreTransientOnCommit) { + ASSERT_TRUE(isTransientTransactionError(ErrorCodes::NoSuchTransaction, + false /* hasWriteConcernError */, + true /* isCommitOrAbort */)); +} + +TEST(ErrorLabelBuilderTest, NonErrorCodesHaveNoLabel) { + OperationSessionInfoFromClient sessionInfo; + std::string commandName = "insert"; + ErrorLabelBuilder builder(sessionInfo, commandName, boost::none, boost::none, false); + ASSERT_FALSE(builder.isTransientTransactionError()); + ASSERT_FALSE(builder.isRetryableWriteError()); + ASSERT_FALSE(builder.isNonResumableChangeStreamError()); +} + +TEST(ErrorLabelBuilderTest, NonTransactionsHaveNoTransientTransactionErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + std::string commandName = "insert"; + ErrorLabelBuilder builder( + sessionInfo, commandName, ErrorCodes::WriteConflict, boost::none, false); + ASSERT_FALSE(builder.isTransientTransactionError()); +} + +TEST(ErrorLabelBuilderTest, RetryableWritesHaveNoTransientTransactionErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + std::string commandName = "insert"; + ErrorLabelBuilder builder( + sessionInfo, commandName, ErrorCodes::WriteConflict, boost::none, false); + ASSERT_FALSE(builder.isTransientTransactionError()); +} + +TEST(ErrorLabelBuilderTest, NonTransientTransactionErrorsHaveNoTransientTransactionErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + sessionInfo.setAutocommit(false); + std::string commandName = "commitTransaction"; + ErrorLabelBuilder builder(sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, false); + ASSERT_FALSE(builder.isTransientTransactionError()); +} + +TEST(ErrorLabelBuilderTest, TransientTransactionErrorsHaveTransientTransactionErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + sessionInfo.setAutocommit(false); + std::string commandName = "commitTransaction"; + ErrorLabelBuilder builder( + sessionInfo, commandName, ErrorCodes::NoSuchTransaction, boost::none, false); + ASSERT_TRUE(builder.isTransientTransactionError()); +} + +TEST(ErrorLabelBuilderTest, NonRetryableWritesHaveNoRetryableWriteErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + std::string commandName = "insert"; + ErrorLabelBuilder builder(sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, false); + + // Test regular writes. + ASSERT_FALSE(builder.isRetryableWriteError()); + + // Test transaction writes. + sessionInfo.setTxnNumber(1); + sessionInfo.setAutocommit(false); + ASSERT_FALSE(builder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, NonRetryableWriteErrorsHaveNoRetryableWriteErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + std::string commandName = "update"; + ErrorLabelBuilder builder( + sessionInfo, commandName, ErrorCodes::WriteConflict, boost::none, false); + ASSERT_FALSE(builder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, RetryableWriteErrorsHaveRetryableWriteErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + std::string commandName = "update"; + ErrorLabelBuilder builder(sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, false); + ASSERT_TRUE(builder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, RetryableWriteErrorsHaveNoRetryableWriteErrorLabelForInternalClients) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + std::string commandName = "update"; + ErrorLabelBuilder builder(sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, true); + ASSERT_FALSE(builder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, + NonRetryableWriteErrorsInWriteConcernErrorsHaveNoRetryableWriteErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + std::string commandName = "update"; + ErrorLabelBuilder builder(sessionInfo, + commandName, + ErrorCodes::WriteConcernFailed, + ErrorCodes::WriteConcernFailed, + false); + ASSERT_FALSE(builder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, RetryableWriteErrorsInWriteConcernErrorsHaveRetryableWriteErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + std::string commandName = "update"; + ErrorLabelBuilder builder(sessionInfo, + commandName, + ErrorCodes::WriteConcernFailed, + ErrorCodes::PrimarySteppedDown, + false); + ASSERT_TRUE(builder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, RetryableWriteErrorsOnCommitAbortHaveRetryableWriteErrorLabel) { + OperationSessionInfoFromClient sessionInfo; + sessionInfo.setTxnNumber(1); + sessionInfo.setAutocommit(false); + std::string commandName; + + commandName = "commitTransaction"; + ErrorLabelBuilder commitBuilder( + sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, false); + ASSERT_TRUE(commitBuilder.isRetryableWriteError()); + + commandName = "coordinateCommitTransaction"; + ErrorLabelBuilder coordinateCommitBuilder( + sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, false); + ASSERT_TRUE(coordinateCommitBuilder.isRetryableWriteError()); + + commandName = "abortTransaction"; + ErrorLabelBuilder abortBuilder( + sessionInfo, commandName, ErrorCodes::NotMaster, boost::none, false); + ASSERT_TRUE(abortBuilder.isRetryableWriteError()); +} + +TEST(ErrorLabelBuilderTest, NonResumableChangeStreamError) { + OperationSessionInfoFromClient sessionInfo; + std::string commandName; + ErrorLabelBuilder builder( + sessionInfo, commandName, ErrorCodes::ChangeStreamHistoryLost, boost::none, false); + ASSERT_TRUE(builder.isNonResumableChangeStreamError()); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/handle_request_response.cpp b/src/mongo/db/handle_request_response.cpp deleted file mode 100644 index 6628c60e8bc..00000000000 --- a/src/mongo/db/handle_request_response.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * . - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/db/handle_request_response.h" -#include "mongo/base/transaction_error.h" - -namespace mongo { - -BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions, - const std::string& commandName, - ErrorCodes::Error code, - bool hasWriteConcernError) { - BSONArrayBuilder labelArray; - - // Note that we only apply the TransientTxnError label if the "autocommit" field is present in - // the session options. When present, "autocommit" will always be false, so we don't check its - // value. - if (sessionOptions.getAutocommit() && - isTransientTransactionError(code, - hasWriteConcernError, - commandName == "commitTransaction" || - commandName == "coordinateCommitTransaction")) { - // An error code for which isTransientTransactionError() is true indicates a transaction - // failure with no persistent side effects. - labelArray << txn::TransientTxnErrorFieldName; - } - - if (ErrorCodes::isNonResumableChangeStreamError(code)) { - labelArray << "NonResumableChangeStreamError"; - } - - return (labelArray.arrSize() > 0) ? BSON("errorLabels" << labelArray.arr()) : BSONObj(); -} - -} // namespace mongo diff --git a/src/mongo/db/handle_request_response.h b/src/mongo/db/handle_request_response.h deleted file mode 100644 index 859a8a35cf3..00000000000 --- a/src/mongo/db/handle_request_response.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * . - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include "mongo/db/logical_session_id.h" - -namespace mongo { - -/** - * Returns the error labels for the given error. - */ -BSONObj getErrorLabels(const OperationSessionInfoFromClient& sessionOptions, - const std::string& commandName, - ErrorCodes::Error code, - bool hasWriteConcernError); - -} // namespace mongo diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index 3f22f0fd379..0876a5598df 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -34,7 +34,6 @@ #include #include "mongo/base/checked_cast.h" -#include "mongo/base/transaction_error.h" #include "mongo/db/audit.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/catalog/collection.h" @@ -46,6 +45,7 @@ #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/curop_failpoint_helpers.h" #include "mongo/db/curop_metrics.h" +#include "mongo/db/error_labels.h" #include "mongo/db/exec/delete.h" #include "mongo/db/exec/update_stage.h" #include "mongo/db/introspect.h" @@ -248,7 +248,7 @@ bool handleError(OperationContext* opCtx, auto txnParticipant = TransactionParticipant::get(opCtx); if (txnParticipant && opCtx->inMultiDocumentTransaction()) { if (isTransientTransactionError( - ex.code(), false /* hasWriteConcernError */, false /* isCommitTransaction */)) { + ex.code(), false /* hasWriteConcernError */, false /* isCommitOrAbort */)) { // Tell the client to try the whole txn again, by returning ok: 0 with errorLabels. throw; } diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 6fb5557d3d4..c64241e5bff 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -49,7 +49,7 @@ #include "mongo/db/curop_metrics.h" #include "mongo/db/cursor_manager.h" #include "mongo/db/dbdirectclient.h" -#include "mongo/db/handle_request_response.h" +#include "mongo/db/error_labels.h" #include "mongo/db/initialize_operation_session_info.h" #include "mongo/db/introspect.h" #include "mongo/db/jsobj.h" @@ -706,17 +706,22 @@ bool runCommandImpl(OperationContext* opCtx, }(); behaviors.attachCurOpErrInfo(opCtx, replyBuilder->getBodyBuilder().asTempObj()); - if (!ok) { + { + boost::optional wcCode; + boost::optional code; auto response = replyBuilder->getBodyBuilder().asTempObj(); auto codeField = response["code"]; - const auto hasWriteConcern = response.hasField("writeConcernError"); - if (codeField.isNumber()) { - auto code = ErrorCodes::Error(codeField.numberInt()); - // Append the error labels for transient transaction errors. - auto errorLabels = - getErrorLabels(sessionOptions, command->getName(), code, hasWriteConcern); - replyBuilder->getBodyBuilder().appendElements(errorLabels); + if (!ok && codeField.isNumber()) { + code = ErrorCodes::Error(codeField.numberInt()); + } + if (response.hasField("writeConcernError")) { + wcCode = ErrorCodes::Error(response["writeConcernError"]["code"].numberInt()); } + auto isInternalClient = opCtx->getClient()->session() && + (opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient); + auto errorLabels = + getErrorLabels(sessionOptions, command->getName(), code, wcCode, isInternalClient); + replyBuilder->getBodyBuilder().appendElements(errorLabels); } auto commandBodyBob = replyBuilder->getBodyBuilder(); @@ -1013,9 +1018,14 @@ void execCommandDatabase(OperationContext* opCtx, // Append the error labels for transient transaction errors. auto response = extraFieldsBuilder.asTempObj(); - auto hasWriteConcern = response.hasField("writeConcernError"); + boost::optional wcCode; + if (response.hasField("writeConcernError")) { + wcCode = ErrorCodes::Error(response["writeConcernError"]["code"].numberInt()); + } + auto isInternalClient = opCtx->getClient()->session() && + (opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient); auto errorLabels = - getErrorLabels(sessionOptions, command->getName(), e.code(), hasWriteConcern); + getErrorLabels(sessionOptions, command->getName(), e.code(), wcCode, isInternalClient); extraFieldsBuilder.appendElements(errorLabels); BSONObjBuilder metadataBob; -- cgit v1.2.1