diff options
author | Antonio Fuschetto <antonio.fuschetto@mongodb.com> | 2022-10-05 09:41:19 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-10-05 10:42:35 +0000 |
commit | 59053967edeea2ace11b0eb9fbe4542dc56a0cab (patch) | |
tree | e4f907e2a1c091055c9cc3ab9bceaaafed802cda | |
parent | 507a15787ce1842f2b8363b6274bbbf8c137fecc (diff) | |
download | mongo-59053967edeea2ace11b0eb9fbe4542dc56a0cab.tar.gz |
SERVER-66972 Database critical section does not serialize with ongoing refreshes
-rw-r--r-- | src/mongo/base/error_codes.yml | 1 | ||||
-rw-r--r-- | src/mongo/db/s/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/s/database_sharding_state.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/s/database_sharding_state.h | 45 | ||||
-rw-r--r-- | src/mongo/db/s/database_sharding_state_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/s/resharding/resharding_destined_recipient_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/s/shard_filtering_metadata_refresh.cpp | 300 | ||||
-rw-r--r-- | src/mongo/db/s/shard_filtering_metadata_refresh.h | 2 | ||||
-rw-r--r-- | src/mongo/db/s/shard_server_op_observer.cpp | 10 |
9 files changed, 318 insertions, 72 deletions
diff --git a/src/mongo/base/error_codes.yml b/src/mongo/base/error_codes.yml index 67d05e909bb..042c8d3899f 100644 --- a/src/mongo/base/error_codes.yml +++ b/src/mongo/base/error_codes.yml @@ -488,6 +488,7 @@ error_codes: - {code: 376, name: ChangeStreamNotEnabled} - {code: 377, name: FLEMaxTagLimitExceeded } - {code: 378, name: NonConformantBSON, categories: [ValidationError]} + - {code: 379, name: DatabaseMetadataRefreshCanceled, categories: [InternalOnly]} # Error codes 4000-8999 are reserved. diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index d34b19aa147..a47874ee8e1 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -176,6 +176,7 @@ env.Library( '$BUILD_DIR/mongo/s/query/cluster_aggregate', '$BUILD_DIR/mongo/s/sharding_api', '$BUILD_DIR/mongo/s/sharding_initialization', + 'forwardable_operation_metadata', 'sharding_api_d', 'sharding_catalog_manager', 'transaction_coordinator', diff --git a/src/mongo/db/s/database_sharding_state.cpp b/src/mongo/db/s/database_sharding_state.cpp index dc340b7f2cf..ab9545ad96c 100644 --- a/src/mongo/db/s/database_sharding_state.cpp +++ b/src/mongo/db/s/database_sharding_state.cpp @@ -101,10 +101,12 @@ std::shared_ptr<DatabaseShardingState> DatabaseShardingState::getSharedForLockFr } void DatabaseShardingState::enterCriticalSectionCatchUpPhase(OperationContext* opCtx, - DSSLock&, + DSSLock& dssLock, const BSONObj& reason) { invariant(opCtx->lockState()->isDbLockedForMode(DatabaseName(boost::none, _dbName), MODE_X)); _critSec.enterCriticalSectionCatchUpPhase(reason); + + cancelDbMetadataRefresh(dssLock); } void DatabaseShardingState::enterCriticalSectionCommitPhase(OperationContext* opCtx, @@ -139,4 +141,27 @@ void DatabaseShardingState::clearMovePrimarySourceManager(OperationContext* opCt _sourceMgr = nullptr; } +void DatabaseShardingState::setDbMetadataRefreshFuture(SharedSemiFuture<void> future, + CancellationSource cancellationSource, + const DSSLock&) { + invariant(!_dbMetadataRefresh); + _dbMetadataRefresh.emplace(std::move(future), std::move(cancellationSource)); +} + +boost::optional<SharedSemiFuture<void>> DatabaseShardingState::getDbMetadataRefreshFuture( + const DSSLock&) const { + return _dbMetadataRefresh ? boost::optional<SharedSemiFuture<void>>(_dbMetadataRefresh->future) + : boost::none; +} + +void DatabaseShardingState::resetDbMetadataRefreshFuture(const DSSLock&) { + _dbMetadataRefresh = boost::none; +} + +void DatabaseShardingState::cancelDbMetadataRefresh(const DSSLock&) { + if (_dbMetadataRefresh) { + _dbMetadataRefresh->cancellationSource.cancel(); + } +} + } // namespace mongo diff --git a/src/mongo/db/s/database_sharding_state.h b/src/mongo/db/s/database_sharding_state.h index a703acb4ce2..56d896a2720 100644 --- a/src/mongo/db/s/database_sharding_state.h +++ b/src/mongo/db/s/database_sharding_state.h @@ -74,6 +74,13 @@ public: StringData dbName); /** + * Returns the name of the database related to the current sharding state. + */ + std::string getDbName() const { + return _dbName; + } + + /** * Methods to control the databases's critical section. Must be called with the database X lock * held. */ @@ -110,9 +117,43 @@ public: */ void clearMovePrimarySourceManager(OperationContext* opCtx); + /** + * Sets the database metadata refresh future for other threads to wait on it. + */ + void setDbMetadataRefreshFuture(SharedSemiFuture<void> future, + CancellationSource cancellationSource, + const DSSLock&); + + /** + * If there is an ongoing database metadata refresh, returns the future to wait on it, otherwise + * `boost::none`. + */ + boost::optional<SharedSemiFuture<void>> getDbMetadataRefreshFuture(const DSSLock&) const; + + /** + * Resets the database metadata refresh future to `boost::none`. + */ + void resetDbMetadataRefreshFuture(const DSSLock&); + + /** + * Cancel any ongoing database metadata refresh. + */ + void cancelDbMetadataRefresh(const DSSLock&); + private: friend DSSLock; + struct DbMetadataRefresh { + DbMetadataRefresh(SharedSemiFuture<void> future, CancellationSource cancellationSource) + : future(std::move(future)), cancellationSource(std::move(cancellationSource)){}; + + // Tracks the ongoing database metadata refresh. + SharedSemiFuture<void> future; + + // Cancellation source to cancel the ongoing database metadata refresh. + CancellationSource cancellationSource; + }; + // Object-wide ResourceMutex to protect changes to the DatabaseShardingState or objects held // within. Lock::ResourceMutex _stateChangeMutex{"DatabaseShardingState"}; @@ -131,6 +172,10 @@ private: // // NOTE: The source manager is not owned by this class. MovePrimarySourceManager* _sourceMgr{nullptr}; + + // Tracks the ongoing database metadata refresh. Possibly keeps a future for other threads to + // wait on it, and a cancellation source to cancel the ongoing database metadata refresh. + boost::optional<DbMetadataRefresh> _dbMetadataRefresh; }; } // namespace mongo diff --git a/src/mongo/db/s/database_sharding_state_test.cpp b/src/mongo/db/s/database_sharding_state_test.cpp index 41f6e5a6147..fb5e249d810 100644 --- a/src/mongo/db/s/database_sharding_state_test.cpp +++ b/src/mongo/db/s/database_sharding_state_test.cpp @@ -166,7 +166,7 @@ TEST_F(DatabaseShardingStateTestWithMockedLoader, ForceDatabaseRefresh) { auto opCtx = operationContext(); _mockCatalogCacheLoader->setDatabaseRefreshReturnValue(newDb); - forceDatabaseRefresh(opCtx, kDbName); + ASSERT_OK(onDbVersionMismatchNoExcept(opCtx, kDbName, boost::none)); boost::optional<DatabaseVersion> activeDbVersion = [&] { AutoGetDb autoDb(opCtx, DatabaseName(boost::none, kDbName), MODE_IS); diff --git a/src/mongo/db/s/resharding/resharding_destined_recipient_test.cpp b/src/mongo/db/s/resharding/resharding_destined_recipient_test.cpp index 614accf368a..9eb59c869e5 100644 --- a/src/mongo/db/s/resharding/resharding_destined_recipient_test.cpp +++ b/src/mongo/db/s/resharding/resharding_destined_recipient_test.cpp @@ -236,7 +236,7 @@ protected: createChunks(env.version.epoch(), env.sourceUuid, env.version.getTimestamp(), "y"), boost::none); - forceDatabaseRefresh(opCtx, kNss.db()); + ASSERT_OK(onDbVersionMismatchNoExcept(opCtx, kNss.db(), boost::none)); forceShardFilteringMetadataRefresh(opCtx, kNss); if (refreshTempNss) diff --git a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp index 8458f0ccb2e..86ab7a41866 100644 --- a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp +++ b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp @@ -37,6 +37,7 @@ #include "mongo/db/operation_context.h" #include "mongo/db/s/collection_sharding_runtime.h" #include "mongo/db/s/database_sharding_state.h" +#include "mongo/db/s/forwardable_operation_metadata.h" #include "mongo/db/s/migration_source_manager.h" #include "mongo/db/s/migration_util.h" #include "mongo/db/s/operation_sharding_state.h" @@ -58,9 +59,165 @@ MONGO_FAIL_POINT_DEFINE(skipDatabaseVersionMetadataRefresh); MONGO_FAIL_POINT_DEFINE(skipShardFilteringMetadataRefresh); MONGO_FAIL_POINT_DEFINE(hangInRecoverRefreshThread); +/** + * If the critical section associate with the database is entered by another thread (e.g., a move + * primary or a drop database is in progress), it releases the acquired locks and waits for the + * latter to exit. In this case the function returns true, otherwise false. + */ +bool checkAndWaitIfCriticalSectionIsEntered( + OperationContext* opCtx, + DatabaseShardingState* dss, + boost::optional<Lock::DBLock>& dbLock, + boost::optional<DatabaseShardingState::DSSLock>& dssLock) { + invariant(dbLock); + invariant(dssLock); + + if (auto critSect = + dss->getCriticalSectionSignal(ShardingMigrationCriticalSection::kRead, *dssLock)) { + LOGV2_DEBUG(6697201, + 2, + "Waiting for exit from the critical section", + "db"_attr = dss->getDbName(), + "reason"_attr = dss->getCriticalSectionReason(*dssLock)); + + dbLock = boost::none; + dssLock = boost::none; + + uassertStatusOK(OperationShardingState::waitForCriticalSectionToComplete(opCtx, *critSect)); + + return true; + } + + return false; +} + +/** + * If another thread is refreshing the database metadata, it releases the acquired locks and waits + * for that refresh to complete. In this case the function returns true, otherwise false. + */ +bool checkAndWaitIfAnotherRefreshIsRunning( + OperationContext* opCtx, + DatabaseShardingState* dss, + boost::optional<Lock::DBLock>& dbLock, + boost::optional<DatabaseShardingState::DSSLock>& dssLock) { + invariant(dbLock); + invariant(dssLock); + + if (auto refreshVersionFuture = dss->getDbMetadataRefreshFuture(*dssLock)) { + LOGV2_DEBUG(6697202, + 2, + "Waiting for completion of another database metadata refresh", + "db"_attr = dss->getDbName()); + + dbLock = boost::none; + dssLock = boost::none; + + try { + refreshVersionFuture->get(opCtx); + } catch (const ExceptionFor<ErrorCodes::DatabaseMetadataRefreshCanceled>&) { + // The refresh was canceled by another thread that entered the critical section. + } + + return true; + } + + return false; +} + +/** + * Unconditionally refreshes the database metadata from the config server. + * + * NOTE: Does network I/O and acquires the database lock in X mode. + */ +Status refreshDbMetadata(OperationContext* opCtx, + const DatabaseName& dbName, + CancellationToken cancellationToken) { + invariant(!opCtx->lockState()->isLocked()); + invariant(!opCtx->getClient()->isInDirectClient()); + invariant(ShardingState::get(opCtx)->canAcceptShardedCommands()); + + ScopeGuard resetRefreshFutureOnError([&] { + UninterruptibleLockGuard noInterrupt(opCtx->lockState()); + + Lock::DBLock dbLock(opCtx, dbName, MODE_IS); + auto* dss = DatabaseShardingState::get(opCtx, dbName.db()); + const auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, dss); + + dss->resetDbMetadataRefreshFuture(dssLock); + }); + + // Force a refresh of the cached database metadata from the config server. + const auto swDbMetadata = + Grid::get(opCtx)->catalogCache()->getDatabaseWithRefresh(opCtx, dbName.db()); + + Lock::DBLock dbLock(opCtx, dbName, MODE_X); + auto* dss = DatabaseShardingState::get(opCtx, dbName.db()); + const auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, dss); + + if (!cancellationToken.isCanceled()) { + if (swDbMetadata.isOK()) { + // Set the refreshed database metadata in the local catalog. + DatabaseHolder::get(opCtx)->openDb(opCtx, dbName); + DatabaseHolder::get(opCtx)->setDbInfo(opCtx, dbName, *swDbMetadata.getValue()); + } else if (swDbMetadata == ErrorCodes::NamespaceNotFound) { + // The database has been dropped, so clear its metadata in the local catalog. + DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, dbName); + } + } + + // Reset the future reference to allow any other thread to refresh the database metadata. + dss->resetDbMetadataRefreshFuture(dssLock); + resetRefreshFutureOnError.dismiss(); + + return swDbMetadata.getStatus(); +} + +SharedSemiFuture<void> asyncDbMetadataRefresh(OperationContext* opCtx, + const DatabaseName& dbName, + const CancellationToken& cancellationToken) { + const auto executor = Grid::get(opCtx)->getExecutorPool()->getFixedExecutor(); + return ExecutorFuture<void>(executor) + .then([=, + serviceCtx = opCtx->getServiceContext(), + forwardableOpMetadata = ForwardableOperationMetadata(opCtx)] { + ThreadClient tc("DbMetadataRefreshThread", serviceCtx); + { + stdx::lock_guard<Client> lk(*tc.get()); + tc->setSystemOperationKillableByStepdown(lk); + } + + const auto opCtxHolder = + CancelableOperationContext(tc->makeOperationContext(), cancellationToken, executor); + auto opCtx = opCtxHolder.get(); + + // Forward `users` and `roles` attributes from the original request. + forwardableOpMetadata.setOn(opCtx); + + LOGV2_DEBUG(6697203, 2, "Started database metadata refresh", "db"_attr = dbName); + + return refreshDbMetadata(opCtx, dbName, cancellationToken); + }) + .onCompletion([=](Status status) { + uassert(ErrorCodes::DatabaseMetadataRefreshCanceled, + str::stream() << "Canceled metadata refresh for database " << dbName, + !cancellationToken.isCanceled()); + + if (status.isOK()) { + LOGV2(6697204, "Refreshed database metadata", "db"_attr = dbName); + } else { + LOGV2_ERROR(6697205, + "Failed database metadata refresh", + "db"_attr = dbName, + "error"_attr = redact(status)); + } + }) + .semi() + .share(); +} + void onDbVersionMismatch(OperationContext* opCtx, const StringData dbName, - boost::optional<DatabaseVersion> clientDbVersion) { + boost::optional<DatabaseVersion> receivedVersion) { invariant(!opCtx->lockState()->isLocked()); invariant(!opCtx->getClient()->isInDirectClient()); invariant(ShardingState::get(opCtx)->canAcceptShardedCommands()); @@ -74,28 +231,86 @@ void onDbVersionMismatch(OperationContext* opCtx, CurOp::get(opCtx)->debug().databaseVersionRefreshMillis += Milliseconds(t.millis()); }); - { - // Take the DBLock directly rather than using AutoGetDb, to prevent a recursive call into - // checkDbVersion(). - // - // TODO: It is not safe here to read the DB version without checking for critical section - // - if (clientDbVersion) { - // TODO SERVER-67440 Use dbName directly - Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, dbName), MODE_IS); - const auto serverDbVersion = DatabaseHolder::get(opCtx)->getDbVersion(opCtx, dbName); - if (clientDbVersion <= serverDbVersion) { - // The client was stale + LOGV2_DEBUG(6697200, + 2, + "Handle database version mismatch", + "db"_attr = dbName, + "receivedVersion"_attr = receivedVersion); + + while (true) { + boost::optional<SharedSemiFuture<void>> dbMetadataRefreshFuture; + + // Set the database metadata refresh future after waiting for another thread to exit from + // the critical section or to complete an ongoing refresh. + { + auto dbLock = boost::make_optional(Lock::DBLock(opCtx, dbName, MODE_IS)); + auto* dss = DatabaseShardingState::get(opCtx, dbName); + + if (receivedVersion) { + auto dssLock = + boost::make_optional(DatabaseShardingState::DSSLock::lockShared(opCtx, dss)); + + if (checkAndWaitIfCriticalSectionIsEntered(opCtx, dss, dbLock, dssLock) || + checkAndWaitIfAnotherRefreshIsRunning(opCtx, dss, dbLock, dssLock)) { + // Waited for another thread to exit from the critical section or to complete an + // ongoing refresh, so reacquire the locks. + continue; + } + + // From now until the end of this block [1] no thread is in the critical section or + // can enter it (would require to X-lock the database) and [2] no metadata refresh + // is in progress or can start (would require to exclusive lock the DSS). + // Therefore, the database version can be accessed safely. + + const auto wantedVersion = DatabaseHolder::get(opCtx)->getDbVersion(opCtx, dbName); + if (receivedVersion <= wantedVersion) { + // No need to refresh the database metadata as the wanted version is newer + // than the one received. + return; + } + } + + if (MONGO_unlikely(skipDatabaseVersionMetadataRefresh.shouldFail())) { return; } + + auto dssLock = + boost::make_optional(DatabaseShardingState::DSSLock::lockExclusive(opCtx, dss)); + + if (checkAndWaitIfCriticalSectionIsEntered(opCtx, dss, dbLock, dssLock) || + checkAndWaitIfAnotherRefreshIsRunning(opCtx, dss, dbLock, dssLock)) { + // Waited for another thread to exit from the critical section or to complete an + // ongoing refresh, so reacquire the locks. + continue; + } + + // From now until the end of this block [1] no thread is in the critical section or can + // enter it (would require to X-lock the database) and [2] this is the only metadata + // refresh in progress (holding the exclusive lock on the DSS). + // Therefore, the future to refresh the database metadata can be set. + + CancellationSource cancellationSource; + CancellationToken cancellationToken = cancellationSource.token(); + dss->setDbMetadataRefreshFuture( + asyncDbMetadataRefresh(opCtx, dbName, cancellationToken), + std::move(cancellationSource), + *dssLock); + dbMetadataRefreshFuture = dss->getDbMetadataRefreshFuture(*dssLock); } - } - if (MONGO_unlikely(skipDatabaseVersionMetadataRefresh.shouldFail())) { - return; - } + // No other metadata refresh for this database can run in parallel. If another thread enters + // the critical section, the ongoing refresh would be interrupted and subsequently + // re-queued. + + try { + dbMetadataRefreshFuture->get(opCtx); + } catch (const ExceptionFor<ErrorCodes::DatabaseMetadataRefreshCanceled>&) { + // The refresh was canceled by another thread that entered the critical section. + continue; + } - forceDatabaseRefresh(opCtx, dbName); + break; + } } // Return true if joins a shard version update/recover/refresh (in that case, all locks are dropped) @@ -486,53 +701,4 @@ Status onDbVersionMismatchNoExcept(OperationContext* opCtx, } } -void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) { - invariant(!opCtx->lockState()->isLocked()); - invariant(!opCtx->getClient()->isInDirectClient()); - - auto const shardingState = ShardingState::get(opCtx); - invariant(shardingState->canAcceptShardedCommands()); - - const auto swRefreshedDbInfo = - Grid::get(opCtx)->catalogCache()->getDatabaseWithRefresh(opCtx, dbName); - - if (swRefreshedDbInfo == ErrorCodes::NamespaceNotFound) { - // db has been dropped, set the db version to boost::none - // TODO SERVER-67440 Use dbName directly - Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, dbName), MODE_X); - DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, dbName); - return; - } - - const auto refreshedDbInfo = uassertStatusOK(std::move(swRefreshedDbInfo)); - const auto& refreshedDBVersion = refreshedDbInfo->getVersion(); - - // First, check under a shared lock if another thread already updated the cached version. - // This is a best-effort optimization to make as few threads as possible to convoy on the - // exclusive lock below. - { - // Take the DBLock directly rather than using AutoGetDb, to prevent a recursive call - // into checkDbVersion(). - // TODO SERVER-67440 Use dbName directly - Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, dbName), MODE_IS); - const auto cachedDbVersion = DatabaseHolder::get(opCtx)->getDbVersion(opCtx, dbName); - if (cachedDbVersion && *cachedDbVersion >= refreshedDBVersion) { - LOGV2_DEBUG(5369130, - 2, - "Skipping updating cached database info from refreshed version " - "because the one currently cached is more recent", - "db"_attr = dbName, - "refreshedDbVersion"_attr = refreshedDBVersion, - "cachedDbVersion"_attr = *cachedDbVersion); - return; - } - } - - // The cached version is older than the refreshed version; update the cached version. - // TODO SERVER-67440 Use dbName directly - Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, dbName), MODE_X); - DatabaseHolder::get(opCtx)->openDb(opCtx, dbName); - DatabaseHolder::get(opCtx)->setDbInfo(opCtx, dbName, *refreshedDbInfo); -} - } // namespace mongo diff --git a/src/mongo/db/s/shard_filtering_metadata_refresh.h b/src/mongo/db/s/shard_filtering_metadata_refresh.h index 955eb3ccdee..3ab7f668e6b 100644 --- a/src/mongo/db/s/shard_filtering_metadata_refresh.h +++ b/src/mongo/db/s/shard_filtering_metadata_refresh.h @@ -90,6 +90,4 @@ Status onDbVersionMismatchNoExcept(OperationContext* opCtx, StringData dbName, boost::optional<DatabaseVersion> clientDbVersion) noexcept; -void forceDatabaseRefresh(OperationContext* opCtx, StringData dbName); - } // namespace mongo diff --git a/src/mongo/db/s/shard_server_op_observer.cpp b/src/mongo/db/s/shard_server_op_observer.cpp index 727b9fb01d3..e52959aa68b 100644 --- a/src/mongo/db/s/shard_server_op_observer.cpp +++ b/src/mongo/db/s/shard_server_op_observer.cpp @@ -400,8 +400,13 @@ void ShardServerOpObserver::onUpdate(OperationContext* opCtx, const OplogUpdateE // TODO SERVER-58223: evaluate whether this is safe or whether acquiring the lock can // block. AllowLockAcquisitionOnTimestampedUnitOfWork allowLockAcquisition(opCtx->lockState()); + AutoGetDb autoDb(opCtx, DatabaseName(boost::none, db), MODE_X); DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, DatabaseName(boost::none, db)); + + auto* dss = DatabaseShardingState::get(opCtx, db); + const auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, dss); + dss->cancelDbMetadataRefresh(dssLock); } } @@ -499,8 +504,13 @@ void ShardServerOpObserver::onDelete(OperationContext* opCtx, // TODO SERVER-58223: evaluate whether this is safe or whether acquiring the lock can block. AllowLockAcquisitionOnTimestampedUnitOfWork allowLockAcquisition(opCtx->lockState()); + AutoGetDb autoDb(opCtx, DatabaseName(boost::none, deletedDatabase), MODE_X); DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, DatabaseName(boost::none, deletedDatabase)); + + auto* dss = DatabaseShardingState::get(opCtx, deletedDatabase); + const auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, dss); + dss->cancelDbMetadataRefresh(dssLock); } if (nss == NamespaceString::kServerConfigurationNamespace) { |