summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJordi Serra Torrens <jordi.serra-torrens@mongodb.com>2021-03-23 10:32:39 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-04-06 10:36:03 +0000
commit3aa71ec3ef14d5354850e905600aa5cda2fcbba3 (patch)
treec86e16a709aafbd65d47294b024d4f2ba209b377 /src
parent9f32e927f98cea09100e10e7fd564df725a42deb (diff)
downloadmongo-3aa71ec3ef14d5354850e905600aa5cda2fcbba3.tar.gz
SERVER-54020: ShardInvalidatedForTargeting thrown by resharding's getDestinedRecipient() not being retried by mongos
Diffstat (limited to 'src')
-rw-r--r--src/mongo/base/error_codes.yml6
-rw-r--r--src/mongo/db/error_labels.cpp1
-rw-r--r--src/mongo/db/error_labels_test.cpp6
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp4
-rw-r--r--src/mongo/db/s/resharding_destined_recipient_test.cpp11
-rw-r--r--src/mongo/db/s/resharding_util.cpp19
-rw-r--r--src/mongo/db/service_entry_point_common.cpp27
-rw-r--r--src/mongo/db/service_entry_point_common.h4
-rw-r--r--src/mongo/db/service_entry_point_mongod.cpp10
-rw-r--r--src/mongo/embedded/service_entry_point_embedded.cpp6
-rw-r--r--src/mongo/s/SConscript1
-rw-r--r--src/mongo/s/catalog_cache.cpp3
-rw-r--r--src/mongo/s/commands/strategy.cpp20
-rw-r--r--src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.cpp58
-rw-r--r--src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h62
-rw-r--r--src/mongo/s/transaction_router.cpp7
16 files changed, 225 insertions, 20 deletions
diff --git a/src/mongo/base/error_codes.yml b/src/mongo/base/error_codes.yml
index 07a8353e4a7..88732abe837 100644
--- a/src/mongo/base/error_codes.yml
+++ b/src/mongo/base/error_codes.yml
@@ -425,6 +425,12 @@ error_codes:
- {code: 341, name: ReshardCollectionAborted}
- {code: 342, name: ReshardingCriticalSectionTimeout}
+ # ShardCannotRefreshDueToLocksHeld will be retried by the shards. Note that it is not under the
+ # NeedRetargettingError, meaning MongoS will pass it through to the client, because there is no
+ # case where it would be possible for MongoS to retry where MongoD couldn't.
+ - {code: 343, name: ShardCannotRefreshDueToLocksHeld,
+ extra: ShardCannotRefreshDueToLocksHeldInfo}
+
# Error codes 4000-8999 are reserved.
# Non-sequential error codes for compatibility only)
diff --git a/src/mongo/db/error_labels.cpp b/src/mongo/db/error_labels.cpp
index bdb6dc65b20..425e67220df 100644
--- a/src/mongo/db/error_labels.cpp
+++ b/src/mongo/db/error_labels.cpp
@@ -175,6 +175,7 @@ bool isTransientTransactionError(ErrorCodes::Error code,
case ErrorCodes::WriteConflict:
case ErrorCodes::LockTimeout:
case ErrorCodes::PreparedTransactionInProgress:
+ case ErrorCodes::ShardCannotRefreshDueToLocksHeld:
case ErrorCodes::ShardInvalidatedForTargeting:
case ErrorCodes::StaleDbVersion:
case ErrorCodes::TenantMigrationAborted:
diff --git a/src/mongo/db/error_labels_test.cpp b/src/mongo/db/error_labels_test.cpp
index 07b597ffaf6..aa7fa88aefe 100644
--- a/src/mongo/db/error_labels_test.cpp
+++ b/src/mongo/db/error_labels_test.cpp
@@ -69,6 +69,12 @@ TEST(IsTransientTransactionErrorTest, TenantMigrationAbortedIsTransient) {
false /* isCommitOrAbort */));
}
+TEST(IsTransientTransactionErrorTest, ShardCannotRefreshDueToLocksHeldIsTransient) {
+ ASSERT_TRUE(isTransientTransactionError(ErrorCodes::ShardCannotRefreshDueToLocksHeld,
+ false /* hasWriteConcernError */,
+ false /* isCommitOrAbort */));
+}
+
TEST(IsTransientTransactionErrorTest, ShardInvalidatedForTargetingIsTransient) {
ASSERT_TRUE(isTransientTransactionError(ErrorCodes::ShardInvalidatedForTargeting,
false /* hasWriteConcernError */,
diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp
index d36103f03e0..9b3b0a00cde 100644
--- a/src/mongo/db/ops/write_ops_exec.cpp
+++ b/src/mongo/db/ops/write_ops_exec.cpp
@@ -302,6 +302,10 @@ bool handleError(OperationContext* opCtx,
return false;
}
+ if (ex.code() == ErrorCodes::ShardCannotRefreshDueToLocksHeld) {
+ throw;
+ }
+
out->results.emplace_back(ex.toStatus());
return !wholeOp.getOrdered();
}
diff --git a/src/mongo/db/s/resharding_destined_recipient_test.cpp b/src/mongo/db/s/resharding_destined_recipient_test.cpp
index b8a4d7ab845..26eb6ca32d0 100644
--- a/src/mongo/db/s/resharding_destined_recipient_test.cpp
+++ b/src/mongo/db/s/resharding_destined_recipient_test.cpp
@@ -50,6 +50,7 @@
#include "mongo/s/catalog/type_shard.h"
#include "mongo/s/catalog_cache_loader_mock.h"
#include "mongo/s/database_version.h"
+#include "mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h"
#include "mongo/s/shard_id.h"
#include "mongo/unittest/unittest.h"
@@ -308,8 +309,14 @@ TEST_F(DestinedRecipientTest, TestGetDestinedRecipientThrowsOnBlockedRefresh) {
auto collDesc = css->getCollectionDescription(opCtx);
FailPointEnableBlock failPoint("blockCollectionCacheLookup");
- ASSERT_THROWS(getDestinedRecipient(opCtx, kNss, BSON("x" << 2 << "y" << 10), css, collDesc),
- ExceptionFor<ErrorCodes::ShardInvalidatedForTargeting>);
+ ASSERT_THROWS_WITH_CHECK(
+ getDestinedRecipient(opCtx, kNss, BSON("x" << 2 << "y" << 10), css, collDesc),
+ ShardCannotRefreshDueToLocksHeldException,
+ [&](const ShardCannotRefreshDueToLocksHeldException& ex) {
+ const auto refreshInfo = ex.extraInfo<ShardCannotRefreshDueToLocksHeldInfo>();
+ ASSERT(refreshInfo);
+ ASSERT_EQ(refreshInfo->getNss(), env.tempNss);
+ });
}
auto sw = catalogCache()->getCollectionRoutingInfoWithRefresh(opCtx, env.tempNss);
diff --git a/src/mongo/db/s/resharding_util.cpp b/src/mongo/db/s/resharding_util.cpp
index b96711fd30b..9017c5eadb2 100644
--- a/src/mongo/db/s/resharding_util.cpp
+++ b/src/mongo/db/s/resharding_util.cpp
@@ -475,22 +475,15 @@ boost::optional<ShardId> getDestinedRecipient(OperationContext* opCtx,
return boost::none;
bool allowLocks = true;
- auto tempNssRoutingInfo = Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo(
- opCtx,
- constructTemporaryReshardingNss(sourceNss.db(), getCollectionUuid(opCtx, sourceNss)),
- allowLocks);
-
- uassert(ShardInvalidatedForTargetingInfo(sourceNss),
- "Routing information is not available for the temporary resharding collection.",
- tempNssRoutingInfo.getStatus() != ErrorCodes::StaleShardVersion);
-
- uassertStatusOK(tempNssRoutingInfo);
+ auto tempNssRoutingInfo =
+ uassertStatusOK(Grid::get(opCtx)->catalogCache()->getCollectionRoutingInfo(
+ opCtx,
+ constructTemporaryReshardingNss(sourceNss.db(), getCollectionUuid(opCtx, sourceNss)),
+ allowLocks));
auto shardKey = reshardingKeyPattern->extractShardKeyFromDocThrows(fullDocument);
- return tempNssRoutingInfo.getValue()
- .findIntersectingChunkWithSimpleCollation(shardKey)
- .getShardId();
+ return tempNssRoutingInfo.findIntersectingChunkWithSimpleCollation(shardKey).getShardId();
}
bool isFinalOplog(const repl::OplogEntry& oplog) {
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 1e1fead879c..ba5528b2416 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -99,6 +99,7 @@
#include "mongo/rpc/metadata/tracking_metadata.h"
#include "mongo/rpc/op_msg.h"
#include "mongo/rpc/reply_builder_interface.h"
+#include "mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h"
#include "mongo/transport/hello_metrics.h"
#include "mongo/transport/service_executor.h"
#include "mongo/transport/session.h"
@@ -688,6 +689,7 @@ private:
std::unique_ptr<PolymorphicScoped> _scoped;
bool _refreshedDatabase = false;
bool _refreshedCollection = false;
+ bool _refreshedCatalogCache = false;
};
class RunCommandImpl {
@@ -1663,6 +1665,31 @@ Future<void> ExecCommandDatabase::_commandExec() {
}
return s;
+ })
+ .onError<ErrorCodes::ShardCannotRefreshDueToLocksHeld>([this](Status s) -> Future<void> {
+ // This exception can never happen on the config server. Config servers can't receive
+ // SSV either, because they never have commands with shardVersion sent.
+ invariant(serverGlobalParams.clusterRole != ClusterRole::ConfigServer);
+
+ auto opCtx = _execContext->getOpCtx();
+ if (!opCtx->getClient()->isInDirectClient() && !_refreshedCatalogCache) {
+ invariant(!opCtx->lockState()->isLocked());
+
+ auto refreshInfo = s.extraInfo<ShardCannotRefreshDueToLocksHeldInfo>();
+ invariant(refreshInfo);
+
+ const auto refreshed =
+ _execContext->behaviors->refreshCatalogCache(opCtx, *refreshInfo);
+
+ if (refreshed) {
+ _refreshedCatalogCache = true;
+ if (!opCtx->inMultiDocumentTransaction()) {
+ return _commandExec();
+ }
+ }
+ }
+
+ return s;
});
}
diff --git a/src/mongo/db/service_entry_point_common.h b/src/mongo/db/service_entry_point_common.h
index d3edbacae12..9500752fd4a 100644
--- a/src/mongo/db/service_entry_point_common.h
+++ b/src/mongo/db/service_entry_point_common.h
@@ -90,6 +90,10 @@ struct ServiceEntryPointCommon {
virtual bool refreshCollection(OperationContext* opCtx, const StaleConfigInfo& se) const
noexcept = 0;
+ virtual bool refreshCatalogCache(
+ OperationContext* opCtx, const ShardCannotRefreshDueToLocksHeldInfo& refreshInfo) const
+ noexcept = 0;
+
virtual void advanceConfigOpTimeFromRequestMetadata(OperationContext* opCtx) const = 0;
MONGO_WARN_UNUSED_RESULT_FUNCTION virtual std::unique_ptr<PolymorphicScoped>
diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp
index 6cc6546376e..436f2c0e442 100644
--- a/src/mongo/db/service_entry_point_mongod.cpp
+++ b/src/mongo/db/service_entry_point_mongod.cpp
@@ -50,6 +50,7 @@
#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/rpc/metadata/sharding_metadata.h"
#include "mongo/s/grid.h"
+#include "mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h"
#include "mongo/s/stale_exception.h"
namespace mongo {
@@ -231,6 +232,15 @@ public:
return onShardVersionMismatchNoExcept(opCtx, se.getNss(), se.getVersionReceived()).isOK();
}
+ bool refreshCatalogCache(OperationContext* opCtx,
+ const ShardCannotRefreshDueToLocksHeldInfo& refreshInfo) const
+ noexcept override {
+ return Grid::get(opCtx)
+ ->catalogCache()
+ ->getCollectionRoutingInfo(opCtx, refreshInfo.getNss())
+ .isOK();
+ }
+
void advanceConfigOpTimeFromRequestMetadata(OperationContext* opCtx) const override {
// Handle config optime information that may have been sent along with the command.
rpc::advanceConfigOpTimeFromRequestMetadata(opCtx);
diff --git a/src/mongo/embedded/service_entry_point_embedded.cpp b/src/mongo/embedded/service_entry_point_embedded.cpp
index 288acc35a0f..0ff33bc12c0 100644
--- a/src/mongo/embedded/service_entry_point_embedded.cpp
+++ b/src/mongo/embedded/service_entry_point_embedded.cpp
@@ -129,6 +129,12 @@ public:
return false;
}
+ bool refreshCatalogCache(OperationContext* opCtx,
+ const ShardCannotRefreshDueToLocksHeldInfo& refreshInfo) const
+ noexcept override {
+ return false;
+ }
+
void advanceConfigOpTimeFromRequestMetadata(OperationContext* opCtx) const override {}
std::unique_ptr<PolymorphicScoped> scopedOperationCompletionShardingActions(
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript
index cfeef9d4940..fd95dd03c91 100644
--- a/src/mongo/s/SConscript
+++ b/src/mongo/s/SConscript
@@ -180,6 +180,7 @@ env.Library(
'resharding/resharding_feature_flag.idl',
'resharding/resume_token.idl',
'resharding/type_collection_fields.idl',
+ 'shard_cannot_refresh_due_to_locks_held_exception.cpp',
'shard_id.cpp',
'shard_invalidated_for_targeting_exception.cpp',
'sharded_collections_ddl_parameters.idl',
diff --git a/src/mongo/s/catalog_cache.cpp b/src/mongo/s/catalog_cache.cpp
index 7a9f20578d3..8d635ce7caf 100644
--- a/src/mongo/s/catalog_cache.cpp
+++ b/src/mongo/s/catalog_cache.cpp
@@ -48,6 +48,7 @@
#include "mongo/s/grid.h"
#include "mongo/s/is_mongos.h"
#include "mongo/s/mongod_and_mongos_server_parameters_gen.h"
+#include "mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h"
#include "mongo/s/stale_exception.h"
#include "mongo/util/concurrency/with_lock.h"
#include "mongo/util/scopeguard.h"
@@ -184,7 +185,7 @@ StatusWith<ChunkManager> CatalogCache::_getCollectionRoutingInfoAt(
collEntryFuture.get(opCtx),
atClusterTime);
} else {
- return Status{ErrorCodes::StaleShardVersion,
+ return Status{ShardCannotRefreshDueToLocksHeldInfo(nss),
"Routing info refresh did not complete"};
}
}
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index b7b382c6cab..814befda717 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -188,7 +188,8 @@ Future<void> invokeInTransactionRouter(std::shared_ptr<RequestExecutionContext>
if (auto code = status.code(); ErrorCodes::isSnapshotError(code) ||
ErrorCodes::isNeedRetargettingError(code) ||
code == ErrorCodes::ShardInvalidatedForTargeting ||
- code == ErrorCodes::StaleDbVersion) {
+ code == ErrorCodes::StaleDbVersion ||
+ code == ErrorCodes::ShardCannotRefreshDueToLocksHeld) {
// Don't abort on possibly retryable errors.
return;
}
@@ -466,6 +467,7 @@ private:
void _onNeedRetargetting(Status& status);
void _onStaleDbVersion(Status& status);
void _onSnapshotError(Status& status);
+ void _onShardCannotRefreshDueToLocksHeldError(Status& status);
ParseAndRunCommand* const _parc;
@@ -888,7 +890,8 @@ void ParseAndRunCommand::RunAndRetry::_checkRetryForTransaction(Status& status)
} else {
invariant(ErrorCodes::isA<ErrorCategory::NeedRetargettingError>(status) ||
status.code() == ErrorCodes::ShardInvalidatedForTargeting ||
- status.code() == ErrorCodes::StaleDbVersion);
+ status.code() == ErrorCodes::StaleDbVersion ||
+ status.code() == ErrorCodes::ShardCannotRefreshDueToLocksHeld);
if (!txnRouter.canContinueOnStaleShardOrDbError(_parc->_commandName, status)) {
if (status.code() == ErrorCodes::ShardInvalidatedForTargeting) {
@@ -980,6 +983,15 @@ void ParseAndRunCommand::RunAndRetry::_onSnapshotError(Status& status) {
iassert(status);
}
+void ParseAndRunCommand::RunAndRetry::_onShardCannotRefreshDueToLocksHeldError(Status& status) {
+ invariant(status.code() == ErrorCodes::ShardCannotRefreshDueToLocksHeld);
+
+ _checkRetryForTransaction(status);
+
+ if (!_canRetry())
+ iassert(status);
+}
+
void ParseAndRunCommand::RunInvocation::_tapOnError(const Status& status) {
auto opCtx = _parc->_rec->getOpCtx();
const auto command = _parc->_rec->getCommand();
@@ -1026,6 +1038,10 @@ Future<void> ParseAndRunCommand::RunAndRetry::run() {
.onErrorCategory<ErrorCategory::SnapshotError>([this](Status status) {
_onSnapshotError(status);
return run(); // Retry
+ })
+ .onError<ErrorCodes::ShardCannotRefreshDueToLocksHeld>([this](Status status) {
+ _onShardCannotRefreshDueToLocksHeldError(status);
+ return run(); // Retry
});
}
diff --git a/src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.cpp b/src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.cpp
new file mode 100644
index 00000000000..6e338f06d19
--- /dev/null
+++ b/src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.cpp
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2021-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
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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/s/shard_cannot_refresh_due_to_locks_held_exception.h"
+
+#include "mongo/base/init.h"
+#include "mongo/bson/bsonobjbuilder.h"
+
+namespace mongo {
+namespace {
+
+MONGO_INIT_REGISTER_ERROR_EXTRA_INFO(ShardCannotRefreshDueToLocksHeldInfo);
+
+} // namespace
+
+void ShardCannotRefreshDueToLocksHeldInfo::serialize(BSONObjBuilder* bob) const {
+ bob->append(kNssFieldName, _nss.ns());
+}
+
+std::shared_ptr<const ErrorExtraInfo> ShardCannotRefreshDueToLocksHeldInfo::parse(
+ const BSONObj& obj) {
+ return std::make_shared<ShardCannotRefreshDueToLocksHeldInfo>(parseFromCommandError(obj));
+}
+
+ShardCannotRefreshDueToLocksHeldInfo ShardCannotRefreshDueToLocksHeldInfo::parseFromCommandError(
+ const BSONObj& obj) {
+ return ShardCannotRefreshDueToLocksHeldInfo(NamespaceString(obj[kNssFieldName].String()));
+}
+
+} // namespace mongo
diff --git a/src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h b/src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h
new file mode 100644
index 00000000000..a7e4d33aad3
--- /dev/null
+++ b/src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2021-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
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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/base/error_extra_info.h"
+#include "mongo/db/namespace_string.h"
+
+namespace mongo {
+
+class ShardCannotRefreshDueToLocksHeldInfo final : public ErrorExtraInfo {
+public:
+ static constexpr auto code = ErrorCodes::ShardCannotRefreshDueToLocksHeld;
+
+ ShardCannotRefreshDueToLocksHeldInfo(NamespaceString nss) : _nss(std::move(nss)) {}
+
+ const auto& getNss() const {
+ return _nss;
+ }
+
+ void serialize(BSONObjBuilder* bob) const;
+
+ static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj& obj);
+
+ static ShardCannotRefreshDueToLocksHeldInfo parseFromCommandError(const BSONObj& obj);
+
+private:
+ static constexpr auto kNssFieldName = "nss"_sd;
+
+ const NamespaceString _nss;
+};
+
+using ShardCannotRefreshDueToLocksHeldException =
+ ExceptionFor<ErrorCodes::ShardCannotRefreshDueToLocksHeld>;
+
+} // namespace mongo
diff --git a/src/mongo/s/transaction_router.cpp b/src/mongo/s/transaction_router.cpp
index 44a445f3ed3..a6df4060e23 100644
--- a/src/mongo/s/transaction_router.cpp
+++ b/src/mongo/s/transaction_router.cpp
@@ -51,6 +51,7 @@
#include "mongo/s/grid.h"
#include "mongo/s/multi_statement_transaction_requests_sender.h"
#include "mongo/s/router_transactions_metrics.h"
+#include "mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/fail_point.h"
#include "mongo/util/log_with_sampling.h"
@@ -1561,13 +1562,15 @@ void TransactionRouter::Router::_updateLastClientInfo(Client* client) {
bool TransactionRouter::Router::_errorAllowsRetryOnStaleShardOrDb(const Status& status) const {
const auto staleInfo = status.extraInfo<StaleConfigInfo>();
const auto staleDB = status.extraInfo<StaleDbRoutingVersion>();
+ const auto shardCannotRefreshDueToLocksHeldInfo =
+ status.extraInfo<ShardCannotRefreshDueToLocksHeldInfo>();
// We can retry on the first operation of stale config or db routing version error if there was
// only one participant in the transaction because there would only be one request sent, and at
// this point that request has finished so there can't be any outstanding requests that would
// race with a retry
- return (staleInfo || staleDB) && o().participants.size() == 1 &&
- p().latestStmtId == p().firstStmtId;
+ return (staleInfo || staleDB || shardCannotRefreshDueToLocksHeldInfo) &&
+ o().participants.size() == 1 && p().latestStmtId == p().firstStmtId;
}
Microseconds TransactionRouter::TimingStats::getDuration(TickSource* tickSource,