diff options
author | Jordi Serra Torrens <jordi.serra-torrens@mongodb.com> | 2021-03-23 10:32:39 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-04-06 10:36:03 +0000 |
commit | 3aa71ec3ef14d5354850e905600aa5cda2fcbba3 (patch) | |
tree | c86e16a709aafbd65d47294b024d4f2ba209b377 /src/mongo | |
parent | 9f32e927f98cea09100e10e7fd564df725a42deb (diff) | |
download | mongo-3aa71ec3ef14d5354850e905600aa5cda2fcbba3.tar.gz |
SERVER-54020: ShardInvalidatedForTargeting thrown by resharding's getDestinedRecipient() not being retried by mongos
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/base/error_codes.yml | 6 | ||||
-rw-r--r-- | src/mongo/db/error_labels.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/error_labels_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/s/resharding_destined_recipient_test.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/s/resharding_util.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.h | 4 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_mongod.cpp | 10 | ||||
-rw-r--r-- | src/mongo/embedded/service_entry_point_embedded.cpp | 6 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/catalog_cache.cpp | 3 | ||||
-rw-r--r-- | src/mongo/s/commands/strategy.cpp | 20 | ||||
-rw-r--r-- | src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.cpp | 58 | ||||
-rw-r--r-- | src/mongo/s/shard_cannot_refresh_due_to_locks_held_exception.h | 62 | ||||
-rw-r--r-- | src/mongo/s/transaction_router.cpp | 7 |
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, |