diff options
26 files changed, 357 insertions, 201 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index e2ed2b29199..cfc2ef5e665 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -804,6 +804,7 @@ env.Library( env.Library( target='catalog_raii', source=[ + 'catalog/catalog_helper.cpp', 'catalog_raii.cpp', ], LIBDEPS=[ diff --git a/src/mongo/db/catalog/catalog_helper.cpp b/src/mongo/db/catalog/catalog_helper.cpp new file mode 100644 index 00000000000..02d1cb1d1c2 --- /dev/null +++ b/src/mongo/db/catalog/catalog_helper.cpp @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2022-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/db/catalog/catalog_helper.h" + +#include "mongo/db/catalog/database_holder.h" +#include "mongo/db/catalog_raii.h" +#include "mongo/db/s/database_sharding_state.h" +#include "mongo/db/s/operation_sharding_state.h" +#include "mongo/db/s/sharding_migration_critical_section.h" +#include "mongo/db/s/sharding_state.h" +#include "mongo/s/stale_exception.h" + +namespace mongo::catalog_helper { + +void assertMatchingDbVersion(OperationContext* opCtx, const StringData& dbName) { + const auto receivedVersion = OperationShardingState::get(opCtx).getDbVersion(dbName); + if (!receivedVersion) { + return; + } + + { + const auto dss = DatabaseShardingState::getSharedForLockFreeReads(opCtx, dbName); + auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss.get()); + + const auto critSecSignal = dss->getCriticalSectionSignal( + opCtx->lockState()->isWriteLocked() ? ShardingMigrationCriticalSection::kWrite + : ShardingMigrationCriticalSection::kRead, + dssLock); + uassert( + StaleDbRoutingVersion(dbName.toString(), *receivedVersion, boost::none, critSecSignal), + str::stream() << "The critical section for the database " << dbName + << " is acquired with reason: " << dss->getCriticalSectionReason(dssLock), + !critSecSignal); + } + + const auto wantedVersion = DatabaseHolder::get(opCtx)->getDbVersion(opCtx, dbName); + uassert(StaleDbRoutingVersion(dbName.toString(), *receivedVersion, boost::none), + str::stream() << "No cached info for the database " << dbName, + wantedVersion); + + uassert(StaleDbRoutingVersion(dbName.toString(), *receivedVersion, *wantedVersion), + str::stream() << "Version mismatch for the database " << dbName, + *receivedVersion == *wantedVersion); +} + +void assertIsPrimaryShardForDb(OperationContext* opCtx, const StringData& dbName) { + invariant(dbName != NamespaceString::kConfigDb); + + uassert(ErrorCodes::IllegalOperation, + str::stream() << "Received request without the version for the database " << dbName, + OperationShardingState::get(opCtx).hasDbVersion()); + + // Recover the database's information if necessary (not cached or not matching). + AutoGetDb autoDb(opCtx, dbName, MODE_IS); + invariant(autoDb.getDb()); + + const auto primaryShardId = DatabaseHolder::get(opCtx)->getDbPrimary(opCtx, dbName).get(); + const auto thisShardId = ShardingState::get(opCtx)->shardId(); + uassert(ErrorCodes::IllegalOperation, + str::stream() << "This is not the primary shard for the database " << dbName + << ". Expected: " << primaryShardId << " Actual: " << thisShardId, + primaryShardId == thisShardId); +} + +} // namespace mongo::catalog_helper diff --git a/src/mongo/db/catalog/catalog_helper.h b/src/mongo/db/catalog/catalog_helper.h new file mode 100644 index 00000000000..0671884740e --- /dev/null +++ b/src/mongo/db/catalog/catalog_helper.h @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2022-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/string_data.h" +#include "mongo/db/operation_context.h" +#include "mongo/s/database_version.h" + +namespace mongo::catalog_helper { + +/** + * Checks that the cached database version matches the one attached to the operation, which means + * that the operation is routed to the right shard (database owner). + * + * Throws `StaleDbRoutingVersion` exception when the critical section is taken, there is no cached + * database version, or the cached database version does not match the one sent by the client. + */ +void assertMatchingDbVersion(OperationContext* opCtx, const StringData& dbName); + +/** + * Checks that the current shard server is the primary for the given database, throwing + * `IllegalOperation` if it is not. + */ +void assertIsPrimaryShardForDb(OperationContext* opCtx, const StringData& dbName); + +} // namespace mongo::catalog_helper diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h index ffb305b2c4c..f74eb86fd11 100644 --- a/src/mongo/db/catalog/database_holder.h +++ b/src/mongo/db/catalog/database_holder.h @@ -36,15 +36,26 @@ #include "mongo/db/catalog/collection.h" #include "mongo/db/catalog/collection_options.h" #include "mongo/db/database_name.h" +#include "mongo/s/catalog/type_database_gen.h" #include "mongo/s/database_version.h" +#include "mongo/s/shard_id.h" namespace mongo { -class Database; class OperationContext; /** * Registry of opened databases. + * + * This also provides functions to read and write cached information for opened databases, which + * basically includes version and ID of the primary shard for each database. The concurrency model + * of this API is implemented as follows: + * 1. The `Database` class caches the information for the specific database. + * 2. Getter and setter functions exposed by this class return and write, respectively, a copy of + * the cached information for the specific database. + * 3. Getter and setter functions are synchronized with each other using the same mutex used to + * synchronize the database map. This prevents one thread from accessing information from a + * database while another is deleting it, for example. */ class DatabaseHolder { public: @@ -115,6 +126,40 @@ public: * Unlike CollectionCatalog::getAllDbNames(), this returns databases that are empty. */ virtual std::vector<DatabaseName> getNames() = 0; + + /** + * Caches the information of the database with the specific name if the database is open, + * otherwise it does nothing. + * + * The database must be locked in X mode when calling this function. + */ + virtual void setDbInfo(OperationContext* opCtx, + const DatabaseName& dbName, + const DatabaseType& dbInfo) = 0; + + /** + * Clears the cached information of the database with the specific name if the database is open, + * otherwise it does nothing. + * + * The database must be locked in IX mode when calling this function. + */ + virtual void clearDbInfo(OperationContext* opCtx, const DatabaseName& dbName) = 0; + + /** + * Returns the version of the database with the specific name if the database is open and the + * version is known, otherwise it returns `boost::none`. + */ + virtual boost::optional<DatabaseVersion> getDbVersion(OperationContext* opCtx, + const DatabaseName& dbName) const = 0; + + /** + * Returns the primary shard ID of the database with the specific name if the database is open + * and the primary shard ID is known, otherwise it returns `boost::none`. + * + * The database must be locked in IS mode when calling this function. + */ + virtual boost::optional<ShardId> getDbPrimary(OperationContext* opCtx, + const DatabaseName& dbName) const = 0; }; } // namespace mongo diff --git a/src/mongo/db/catalog/database_holder_impl.cpp b/src/mongo/db/catalog/database_holder_impl.cpp index 317c50c88ca..31275bfe514 100644 --- a/src/mongo/db/catalog/database_holder_impl.cpp +++ b/src/mongo/db/catalog/database_holder_impl.cpp @@ -323,4 +323,88 @@ void DatabaseHolderImpl::closeAll(OperationContext* opCtx) { } } +void DatabaseHolderImpl::setDbInfo(OperationContext* opCtx, + const DatabaseName& dbName, + const DatabaseType& dbInfo) { + uassert( + 6420900, + "Invalid database name: " + dbName.db(), + NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); + invariant(opCtx->lockState()->isDbLockedForMode(dbName.db(), MODE_X)); + + stdx::lock_guard<SimpleMutex> lk(_m); + + const auto it = _dbs.find(dbName); + if (it == _dbs.end() || !it->second) { + return; + } + + LOGV2(6420901, + "Setting this node's cached database info", + "db"_attr = dbName.db(), + "version"_attr = dbInfo.getVersion()); + + auto db = static_cast<DatabaseImpl*>(it->second); + db->_info.emplace(dbInfo); +} + +void DatabaseHolderImpl::clearDbInfo(OperationContext* opCtx, const DatabaseName& dbName) { + uassert( + 6420902, + "Invalid database name: " + dbName.db(), + NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); + invariant(opCtx->lockState()->isDbLockedForMode(dbName.db(), MODE_IX)); + + stdx::lock_guard<SimpleMutex> lk(_m); + + const auto it = _dbs.find(dbName); + if (it == _dbs.end() || !it->second) { + return; + } + + LOGV2(6420903, "Clearing this node's cached database info", "db"_attr = dbName.db()); + + auto db = static_cast<DatabaseImpl*>(it->second); + db->_info = boost::none; +} + +boost::optional<DatabaseVersion> DatabaseHolderImpl::getDbVersion( + OperationContext* opCtx, const DatabaseName& dbName) const { + uassert( + 6420904, + "Invalid database name: " + dbName.db(), + NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); + // The database should be required to be locked in IS mode, however this function is also called + // by the `AutoGet*ForReadLockFree` constructor, which only holds the global lock in IS mode. + + stdx::lock_guard<SimpleMutex> lk(_m); + + const auto it = _dbs.find(dbName); + if (it == _dbs.end() || !it->second) { + return boost::none; + } + + auto db = static_cast<DatabaseImpl*>(it->second); + return db->_info ? boost::optional<DatabaseVersion>(db->_info->getVersion()) : boost::none; +} + +boost::optional<ShardId> DatabaseHolderImpl::getDbPrimary(OperationContext* opCtx, + const DatabaseName& dbName) const { + uassert( + 6420905, + "Invalid database name: " + dbName.db(), + NamespaceString::validDBName(dbName.db(), NamespaceString::DollarInDbNameBehavior::Allow)); + invariant(opCtx->lockState()->isDbLockedForMode(dbName.db(), MODE_IS)); + + stdx::lock_guard<SimpleMutex> lk(_m); + + const auto it = _dbs.find(dbName); + if (it == _dbs.end() || !it->second) { + return boost::none; + } + + auto db = static_cast<DatabaseImpl*>(it->second); + return db->_info ? boost::optional<ShardId>(db->_info->getPrimary()) : boost::none; +} + } // namespace mongo diff --git a/src/mongo/db/catalog/database_holder_impl.h b/src/mongo/db/catalog/database_holder_impl.h index 758bc0e4d9c..96500a6f4a5 100644 --- a/src/mongo/db/catalog/database_holder_impl.h +++ b/src/mongo/db/catalog/database_holder_impl.h @@ -60,6 +60,18 @@ public: std::vector<DatabaseName> getNames() override; + void setDbInfo(OperationContext* opCtx, + const DatabaseName& dbName, + const DatabaseType& dbInfo) override; + + void clearDbInfo(OperationContext* opCtx, const DatabaseName& dbName) override; + + boost::optional<DatabaseVersion> getDbVersion(OperationContext* opCtx, + const DatabaseName& dbName) const override; + + boost::optional<ShardId> getDbPrimary(OperationContext* opCtx, + const DatabaseName& dbName) const override; + private: std::set<DatabaseName> _getNamesWithConflictingCasing_inlock(const DatabaseName& dbName); diff --git a/src/mongo/db/catalog/database_holder_mock.h b/src/mongo/db/catalog/database_holder_mock.h index d21558b4be0..f75a12a8a08 100644 --- a/src/mongo/db/catalog/database_holder_mock.h +++ b/src/mongo/db/catalog/database_holder_mock.h @@ -64,6 +64,22 @@ public: std::vector<DatabaseName> getNames() override { return {}; } + + void setDbInfo(OperationContext* opCtx, + const DatabaseName& dbName, + const DatabaseType& dbInfo) override {} + + void clearDbInfo(OperationContext* opCtx, const DatabaseName& dbName) override {} + + boost::optional<DatabaseVersion> getDbVersion(OperationContext* opCtx, + const DatabaseName& dbName) const override { + return boost::none; + } + + boost::optional<ShardId> getDbPrimary(OperationContext* opCtx, + const DatabaseName& dbName) const override { + return boost::none; + } }; } // namespace mongo diff --git a/src/mongo/db/catalog/database_impl.h b/src/mongo/db/catalog/database_impl.h index 6aa7ae6095a..295b1c59525 100644 --- a/src/mongo/db/catalog/database_impl.h +++ b/src/mongo/db/catalog/database_impl.h @@ -31,6 +31,7 @@ #include "mongo/db/catalog/database.h" #include "mongo/db/database_name.h" +#include "mongo/s/catalog/type_database_gen.h" namespace mongo { @@ -120,6 +121,8 @@ public: void checkForIdIndexesAndDropPendingCollections(OperationContext* opCtx) const final; private: + friend class DatabaseHolderImpl; + /** * Throws if there is a reason 'ns' cannot be created as a user collection. Namespace pattern * matching checks should be added to userAllowedCreateNS(). @@ -153,6 +156,9 @@ private: // collections may be created in this Database. // This variable may only be read/written while the database is locked in MODE_X. AtomicWord<bool> _dropPending{false}; + + // Node's cached database info. + boost::optional<DatabaseType> _info; }; } // namespace mongo diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index 260c7ea926b..b2ea5e5ff4a 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -33,6 +33,7 @@ #include "mongo/db/catalog/rename_collection.h" #include "mongo/bson/unordered_fields_bsonobj_comparator.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/catalog/collection_uuid_mismatch.h" #include "mongo/db/catalog/database_holder.h" @@ -480,11 +481,7 @@ Status renameBetweenDBs(OperationContext* opCtx, targetDBLock.emplace(opCtx, target.dbName(), MODE_X); } - { - auto dss = DatabaseShardingState::get(opCtx, source.db()); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - dss->checkDbVersion(opCtx, dssLock); - } + catalog_helper::assertMatchingDbVersion(opCtx, source.db()); DisableDocumentValidation validationDisabler(opCtx); diff --git a/src/mongo/db/catalog_raii.cpp b/src/mongo/db/catalog_raii.cpp index 760bd4fa8fb..c3c87a95844 100644 --- a/src/mongo/db/catalog_raii.cpp +++ b/src/mongo/db/catalog_raii.cpp @@ -32,6 +32,7 @@ #include "mongo/db/catalog_raii.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/s/collection_sharding_state.h" @@ -177,9 +178,7 @@ AutoGetDb::AutoGetDb(OperationContext* opCtx, StringData dbName, LockMode mode, return databaseHolder->getDb(opCtx, tenantDbName); }()) { // The 'primary' database must be version checked for sharding. - auto dss = DatabaseShardingState::get(opCtx, dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - dss->checkDbVersion(opCtx, dssLock); + catalog_helper::assertMatchingDbVersion(opCtx, _dbName); } Database* AutoGetDb::ensureDbExists(OperationContext* opCtx) { @@ -191,9 +190,7 @@ Database* AutoGetDb::ensureDbExists(OperationContext* opCtx) { const DatabaseName dbName(boost::none, _dbName); _db = databaseHolder->openDb(opCtx, dbName, nullptr); - auto dss = DatabaseShardingState::get(opCtx, _dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - dss->checkDbVersion(opCtx, dssLock); + catalog_helper::assertMatchingDbVersion(opCtx, _dbName); return _db; } @@ -379,14 +376,10 @@ AutoGetCollectionLockFree::AutoGetCollectionLockFree(OperationContext* opCtx, return _collection.get(); }); - { - // Check that the sharding database version matches our read. - // Note: this must always be checked, regardless of whether the collection exists, so that - // the dbVersion of this node or the caller gets updated quickly in case either is stale. - auto dss = DatabaseShardingState::getSharedForLockFreeReads(opCtx, _resolvedNss.db()); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss.get()); - dss->checkDbVersion(opCtx, dssLock); - } + // Check that the sharding database version matches our read. + // Note: this must always be checked, regardless of whether the collection exists, so that the + // dbVersion of this node or the caller gets updated quickly in case either is stale. + catalog_helper::assertMatchingDbVersion(opCtx, _resolvedNss.db()); hangBeforeAutoGetCollectionLockFreeShardedStateAccess.executeIf( [&](auto&) { hangBeforeAutoGetCollectionLockFreeShardedStateAccess.pauseWhileSet(opCtx); }, diff --git a/src/mongo/db/commands/internal_rename_if_options_and_indexes_match_cmd.cpp b/src/mongo/db/commands/internal_rename_if_options_and_indexes_match_cmd.cpp index f9648db8105..a374ad2ab6d 100644 --- a/src/mongo/db/commands/internal_rename_if_options_and_indexes_match_cmd.cpp +++ b/src/mongo/db/commands/internal_rename_if_options_and_indexes_match_cmd.cpp @@ -32,6 +32,7 @@ #include "mongo/db/commands/internal_rename_if_options_and_indexes_match_gen.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/catalog/rename_collection.h" #include "mongo/db/commands.h" #include "mongo/db/db_raii.h" @@ -80,7 +81,7 @@ public: } // Check if the receiving shard is still the primary for the database - DatabaseShardingState::checkIsPrimaryShardForDb(opCtx, fromNss.db()); + catalog_helper::assertIsPrimaryShardForDb(opCtx, fromNss.db()); // Acquiring the local part of the distributed locks for involved namespaces allows: // - Serialize with sharded DDLs, ensuring no concurrent modifications of the diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp index 688577f8e28..86490bc9e36 100644 --- a/src/mongo/db/db_raii.cpp +++ b/src/mongo/db/db_raii.cpp @@ -32,6 +32,7 @@ #include "mongo/db/db_raii.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/concurrency/locker.h" @@ -1003,9 +1004,7 @@ AutoGetDbForReadLockFree::AutoGetDbForReadLockFree(OperationContext* opCtx, // Note: this must always be checked, regardless of whether the collection exists, so // that the dbVersion of this node or the caller gets updated quickly in case either is // stale. - auto dss = DatabaseShardingState::getSharedForLockFreeReads(opCtx, dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss.get()); - dss->checkDbVersion(opCtx, dssLock); + catalog_helper::assertMatchingDbVersion(opCtx, dbName); return std::make_pair(&fakeColl, /* isView */ false); }, /* ResetFunc */ diff --git a/src/mongo/db/pipeline/process_interface/shardsvr_process_interface.cpp b/src/mongo/db/pipeline/process_interface/shardsvr_process_interface.cpp index 54955eab412..2acfd4c2012 100644 --- a/src/mongo/db/pipeline/process_interface/shardsvr_process_interface.cpp +++ b/src/mongo/db/pipeline/process_interface/shardsvr_process_interface.cpp @@ -34,6 +34,7 @@ #include <fmt/format.h> +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/db_raii.h" #include "mongo/db/exec/shard_filterer_impl.h" @@ -410,7 +411,7 @@ ShardServerProcessInterface::expectUnshardedCollectionInScope( void ShardServerProcessInterface::checkOnPrimaryShardForDb(OperationContext* opCtx, const NamespaceString& nss) { - DatabaseShardingState::checkIsPrimaryShardForDb(opCtx, nss.db()); + catalog_helper::assertIsPrimaryShardForDb(opCtx, nss.db()); } } // namespace mongo diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index c8fbd314d87..6d0c008243d 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -171,6 +171,7 @@ env.Library( '$BUILD_DIR/mongo/crypto/encrypted_field_config', '$BUILD_DIR/mongo/crypto/fle_crypto', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', + '$BUILD_DIR/mongo/db/catalog/database_holder', '$BUILD_DIR/mongo/db/index_builds_coordinator_interface', '$BUILD_DIR/mongo/db/ops/write_ops', '$BUILD_DIR/mongo/db/repl/image_collection_entry', @@ -450,6 +451,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/bson/dotted_path_support', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', + '$BUILD_DIR/mongo/db/catalog/database_holder', '$BUILD_DIR/mongo/db/cloner', '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_commands_invocation', '$BUILD_DIR/mongo/db/commands/core', @@ -617,6 +619,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/catalog/catalog_test_fixture', + '$BUILD_DIR/mongo/db/catalog/database_holder', '$BUILD_DIR/mongo/db/commands/server_status', '$BUILD_DIR/mongo/db/exec/document_value/document_value_test_util', '$BUILD_DIR/mongo/db/keys_collection_client_direct', diff --git a/src/mongo/db/s/database_sharding_state.cpp b/src/mongo/db/s/database_sharding_state.cpp index d4eabcc197a..fe77d844162 100644 --- a/src/mongo/db/s/database_sharding_state.cpp +++ b/src/mongo/db/s/database_sharding_state.cpp @@ -93,30 +93,6 @@ DatabaseShardingState* DatabaseShardingState::get(OperationContext* opCtx, return databasesMap.getOrCreate(dbName).get(); } -void DatabaseShardingState::checkIsPrimaryShardForDb(OperationContext* opCtx, StringData dbName) { - invariant(dbName != NamespaceString::kConfigDb); - - uassert(ErrorCodes::IllegalOperation, - "Request sent without attaching database version", - OperationShardingState::get(opCtx).hasDbVersion()); - - const auto dbPrimaryShardId = [&]() { - // TODO SERVER-63706 Use dbName directly - Lock::DBLock dbWriteLock(opCtx, DatabaseName(boost::none, dbName), MODE_IS); - auto dss = DatabaseShardingState::get(opCtx, dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - // The following call will also ensure that the database version matches - return dss->getDatabaseInfo(opCtx, dssLock).getPrimary(); - }(); - - const auto thisShardId = ShardingState::get(opCtx)->shardId(); - - uassert(ErrorCodes::IllegalOperation, - str::stream() << "This is not the primary shard for db " << dbName - << " expected: " << dbPrimaryShardId << " shardId: " << thisShardId, - dbPrimaryShardId == thisShardId); -} - std::shared_ptr<DatabaseShardingState> DatabaseShardingState::getSharedForLockFreeReads( OperationContext* opCtx, const StringData dbName) { auto& databasesMap = DatabaseShardingStateMap::get(opCtx->getServiceContext()); @@ -142,70 +118,6 @@ void DatabaseShardingState::exitCriticalSection(OperationContext* opCtx, const B _critSec.exitCriticalSection(reason); } -DatabaseType DatabaseShardingState::getDatabaseInfo(OperationContext* opCtx, - DSSLock& dssLock) const { - checkDbVersion(opCtx, dssLock); - invariant(_optDatabaseInfo); - return _optDatabaseInfo.get(); -} - -boost::optional<DatabaseVersion> DatabaseShardingState::getDbVersion(OperationContext* opCtx, - DSSLock&) const { - if (!opCtx->lockState()->isDbLockedForMode(_dbName, MODE_X)) { - invariant(opCtx->lockState()->isDbLockedForMode(_dbName, MODE_IS)); - } - return (_optDatabaseInfo) ? boost::optional<DatabaseVersion>(_optDatabaseInfo->getVersion()) - : boost::none; -} - -void DatabaseShardingState::clearDatabaseInfo(OperationContext* opCtx) { - LOGV2(5369110, "Clearing node's cached database info", "db"_attr = _dbName); - const auto dssLock = DSSLock::lockExclusive(opCtx, this); - _optDatabaseInfo = boost::none; -} - -void DatabaseShardingState::setDatabaseInfo(OperationContext* opCtx, - DatabaseType&& newDatabaseInfo, - DSSLock& dssLock) { - invariant(opCtx->lockState()->isDbLockedForMode(_dbName, MODE_X)); - LOGV2(5369111, - "Setting this node's cached database info", - "db"_attr = _dbName, - "newDatabaseVersion"_attr = newDatabaseInfo.getVersion()); - _optDatabaseInfo.emplace(std::move(newDatabaseInfo)); -} - -void DatabaseShardingState::checkDbVersion(OperationContext* opCtx, DSSLock&) const { - invariant(opCtx->lockState()->isLocked()); - - const auto clientDbVersion = OperationShardingState::get(opCtx).getDbVersion(_dbName); - if (!clientDbVersion) - return; - - { - auto criticalSectionSignal = _critSec.getSignal( - opCtx->lockState()->isWriteLocked() ? ShardingMigrationCriticalSection::kWrite - : ShardingMigrationCriticalSection::kRead); - const std::string reason = - _critSec.getReason() ? _critSec.getReason()->toString() : "unknown"; - uassert( - StaleDbRoutingVersion(_dbName, *clientDbVersion, boost::none, criticalSectionSignal), - str::stream() << "The critical section for " << _dbName - << " is acquired with reason: " << reason, - !criticalSectionSignal); - } - - uassert(StaleDbRoutingVersion(_dbName, *clientDbVersion, boost::none), - str::stream() << "sharding status of database " << _dbName - << " is not currently known and needs to be recovered", - _optDatabaseInfo); - - const auto& dbVersion = _optDatabaseInfo->getVersion(); - uassert(StaleDbRoutingVersion(_dbName, *clientDbVersion, dbVersion), - str::stream() << "dbVersion mismatch for database " << _dbName, - *clientDbVersion == dbVersion); -} - MovePrimarySourceManager* DatabaseShardingState::getMovePrimarySourceManager(DSSLock&) { return _sourceMgr; } diff --git a/src/mongo/db/s/database_sharding_state.h b/src/mongo/db/s/database_sharding_state.h index 4a263405c2b..a703acb4ce2 100644 --- a/src/mongo/db/s/database_sharding_state.h +++ b/src/mongo/db/s/database_sharding_state.h @@ -74,16 +74,6 @@ public: StringData dbName); /** - * Checks if this shard is the primary shard for the given DB. - * - * Throws an IllegalOperation exception otherwise. - * - * Assumes the operation context has a DB version attached to it for the given @dbName. - */ - static void checkIsPrimaryShardForDb(OperationContext* opCtx, StringData dbName); - - - /** * Methods to control the databases's critical section. Must be called with the database X lock * held. */ @@ -95,38 +85,9 @@ public: return _critSec.getSignal(op); } - /** - * Returns this shard server's cached dbVersion, if one is cached. - * - * Invariants that the caller holds the DBLock in X or IS. - */ - boost::optional<DatabaseVersion> getDbVersion(OperationContext* opCtx, DSSLock&) const; - - /** - * Sets this shard server's cached database info. - * - * Invariants that the caller holds the DBLock in X mode. - */ - void setDatabaseInfo(OperationContext* opCtx, DatabaseType&& newDatabaseInfo, DSSLock&); - - /** - * Resets this shard server's cached database info. - */ - void clearDatabaseInfo(OperationContext* opCtx); - - /** - * Returns this shard server's cached database info. - * Internally performs the same checks of checkDbVersion(), - * so it will throws for the same reasons. - */ - DatabaseType getDatabaseInfo(OperationContext* opCtx, DSSLock&) const; - - /** - * If _critSecSignal is non-null, always throws StaleDbVersion. - * Otherwise, if there is a client dbVersion on the OperationContext, compares it with this - * shard server's cached dbVersion and throws StaleDbVersion if they do not match. - */ - void checkDbVersion(OperationContext* opCtx, DSSLock&) const; + auto getCriticalSectionReason(DSSLock&) const { + return _critSec.getReason() ? _critSec.getReason()->toString() : "Unknown"; + } /** * Returns the active movePrimary source manager, if one is available. @@ -164,9 +125,6 @@ private: ShardingMigrationCriticalSection _critSec; - // This shard server's cached database info. If boost::none - boost::optional<DatabaseType> _optDatabaseInfo; - // If this database is serving as a source shard for a movePrimary, the source manager will be // non-null. To write this value, there needs to be X-lock on the database in order to // synchronize with other callers which will read the source manager. diff --git a/src/mongo/db/s/database_sharding_state_test.cpp b/src/mongo/db/s/database_sharding_state_test.cpp index 81d5218dbd4..a0848154f53 100644 --- a/src/mongo/db/s/database_sharding_state_test.cpp +++ b/src/mongo/db/s/database_sharding_state_test.cpp @@ -30,6 +30,7 @@ #include "mongo/platform/basic.h" #include "boost/optional/optional_io.hpp" +#include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/db_raii.h" #include "mongo/db/repl/wait_for_majority_service.h" @@ -137,9 +138,7 @@ TEST_F(DatabaseShardingStateTestWithMockedLoader, OnDbVersionMismatch) { auto getActiveDbVersion = [&] { AutoGetDb autoDb(opCtx, kDbName, MODE_IS); - const auto dss = DatabaseShardingState::get(opCtx, kDbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - return dss->getDbVersion(opCtx, dssLock); + return DatabaseHolder::get(opCtx)->getDbVersion(opCtx, kDbName); }; _mockCatalogCacheLoader->setDatabaseRefreshReturnValue(newDb); @@ -172,9 +171,7 @@ TEST_F(DatabaseShardingStateTestWithMockedLoader, ForceDatabaseRefresh) { boost::optional<DatabaseVersion> activeDbVersion = [&] { AutoGetDb autoDb(opCtx, kDbName, MODE_IS); - const auto dss = DatabaseShardingState::get(opCtx, kDbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - return dss->getDbVersion(opCtx, dssLock); + return DatabaseHolder::get(opCtx)->getDbVersion(opCtx, kDbName); }(); ASSERT_TRUE(activeDbVersion); if (expectRefresh) { diff --git a/src/mongo/db/s/drop_database_coordinator.cpp b/src/mongo/db/s/drop_database_coordinator.cpp index 708f004f801..a6676e133b4 100644 --- a/src/mongo/db/s/drop_database_coordinator.cpp +++ b/src/mongo/db/s/drop_database_coordinator.cpp @@ -31,6 +31,7 @@ #include "mongo/db/s/drop_database_coordinator.h" #include "mongo/db/api_parameters.h" +#include "mongo/db/catalog/database_holder.h" #include "mongo/db/persistent_task_store.h" #include "mongo/db/s/database_sharding_state.h" #include "mongo/db/s/shard_metadata_util.h" @@ -150,13 +151,6 @@ void DropDatabaseCoordinator::_dropShardedCollection( opCtx, nss, {primaryShardId}, **executor, getCurrentSession()); } -void DropDatabaseCoordinator::_clearDatabaseInfoOnPrimary(OperationContext* opCtx) { - // TODO SERVER-67438 Use _dbName directly once it's of type DatabaseName - Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, _dbName), MODE_X); - auto dss = DatabaseShardingState::get(opCtx, _dbName); - dss->clearDatabaseInfo(opCtx); -} - void DropDatabaseCoordinator::_clearDatabaseInfoOnSecondaries(OperationContext* opCtx) { Status signalStatus = shardmetadatautil::updateShardDatabasesEntry( opCtx, @@ -297,7 +291,6 @@ ExecutorFuture<void> DropDatabaseCoordinator::_runImpl( // Clear the database sharding state info before exiting the critical section so // that all subsequent write operations with the old database version will fail // due to StaleDbVersion. - _clearDatabaseInfoOnPrimary(opCtx); _clearDatabaseInfoOnSecondaries(opCtx); removeDatabaseMetadataFromConfig( diff --git a/src/mongo/db/s/drop_database_coordinator.h b/src/mongo/db/s/drop_database_coordinator.h index 1d5cf77e028..72f2e7647ef 100644 --- a/src/mongo/db/s/drop_database_coordinator.h +++ b/src/mongo/db/s/drop_database_coordinator.h @@ -61,8 +61,6 @@ private: const CollectionType& coll, std::shared_ptr<executor::ScopedTaskExecutor> executor); - void _clearDatabaseInfoOnPrimary(OperationContext* opCtx); - void _clearDatabaseInfoOnSecondaries(OperationContext* opCtx); StringData _dbName; diff --git a/src/mongo/db/s/get_database_version_command.cpp b/src/mongo/db/s/get_database_version_command.cpp index 04b48d16dd4..2b5ebd0ff2a 100644 --- a/src/mongo/db/s/get_database_version_command.cpp +++ b/src/mongo/db/s/get_database_version_command.cpp @@ -34,6 +34,7 @@ #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/privilege.h" +#include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/commands.h" #include "mongo/db/s/database_sharding_state.h" @@ -81,10 +82,8 @@ public: BSONObj versionObj; AutoGetDb autoDb(opCtx, _targetDb(), MODE_IS); - const auto dss = DatabaseShardingState::get(opCtx, _targetDb()); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - - if (auto dbVersion = dss->getDbVersion(opCtx, dssLock)) { + if (const auto dbVersion = + DatabaseHolder::get(opCtx)->getDbVersion(opCtx, _targetDb())) { versionObj = dbVersion->toBSON(); } result->getBodyBuilder().append("dbVersion", versionObj); diff --git a/src/mongo/db/s/move_primary_source_manager.cpp b/src/mongo/db/s/move_primary_source_manager.cpp index aaca2f82bbb..11b8390f55a 100644 --- a/src/mongo/db/s/move_primary_source_manager.cpp +++ b/src/mongo/db/s/move_primary_source_manager.cpp @@ -30,6 +30,7 @@ #include "mongo/db/s/move_primary_source_manager.h" #include "mongo/client/connpool.h" +#include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/commands.h" #include "mongo/db/dbdirectclient.h" @@ -281,8 +282,8 @@ Status MovePrimarySourceManager::commitOnConfig(OperationContext* opCtx) { } if (!repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, getNss())) { - auto dss = DatabaseShardingState::get(opCtx, getNss().db()); - dss->clearDatabaseInfo(opCtx); + DatabaseHolder::get(opCtx)->clearDbInfo( + opCtx, DatabaseName(boost::none, getNss().toString())); uassertStatusOK(validateStatus.withContext( str::stream() << "Unable to verify movePrimary commit for database: " << getNss().ns() @@ -446,7 +447,8 @@ void MovePrimarySourceManager::_cleanup(OperationContext* opCtx) { auto dss = DatabaseShardingState::get(opCtx, getNss().db()); dss->clearMovePrimarySourceManager(opCtx); - dss->clearDatabaseInfo(opCtx); + DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, + DatabaseName(boost::none, getNss().toString())); // Leave the critical section if we're still registered. dss->exitCriticalSection(opCtx, _critSecReason); } diff --git a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp index 60046674966..0895d38bd84 100644 --- a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp +++ b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp @@ -79,9 +79,7 @@ void onDbVersionMismatch(OperationContext* opCtx, if (clientDbVersion) { // TODO SERVER-67440 Use dbName directly Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, dbName), MODE_IS); - auto dss = DatabaseShardingState::get(opCtx, dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - const auto serverDbVersion = dss->getDbVersion(opCtx, dssLock); + const auto serverDbVersion = DatabaseHolder::get(opCtx)->getDbVersion(opCtx, dbName); if (clientDbVersion <= serverDbVersion) { // The client was stale return; @@ -498,8 +496,7 @@ void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) { // 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); - auto dss = DatabaseShardingState::get(opCtx, dbName); - dss->clearDatabaseInfo(opCtx); + DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, dbName); return; } @@ -514,10 +511,7 @@ void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) { // into checkDbVersion(). // TODO SERVER-67440 Use dbName directly Lock::DBLock dbLock(opCtx, DatabaseName(boost::none, dbName), MODE_IS); - auto dss = DatabaseShardingState::get(opCtx, dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockShared(opCtx, dss); - - const auto cachedDbVersion = dss->getDbVersion(opCtx, dssLock); + const auto cachedDbVersion = DatabaseHolder::get(opCtx)->getDbVersion(opCtx, dbName); if (cachedDbVersion && *cachedDbVersion >= refreshedDBVersion) { LOGV2_DEBUG(5369130, 2, @@ -533,10 +527,8 @@ void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) { // 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); - auto dss = DatabaseShardingState::get(opCtx, dbName); - auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, dss); - - dss->setDatabaseInfo(opCtx, DatabaseType(*refreshedDbInfo), dssLock); + DatabaseHolder::get(opCtx)->openDb(opCtx, dbName); + DatabaseHolder::get(opCtx)->setDbInfo(opCtx, dbName, *refreshedDbInfo); } } // 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 4f853ddf78b..b0e82c3e738 100644 --- a/src/mongo/db/s/shard_server_op_observer.cpp +++ b/src/mongo/db/s/shard_server_op_observer.cpp @@ -33,6 +33,7 @@ #include "mongo/db/s/shard_server_op_observer.h" #include "mongo/bson/util/bson_extract.h" +#include "mongo/db/catalog/database_holder_impl.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/op_observer_impl.h" #include "mongo/db/s/balancer_stats_registry.h" @@ -395,8 +396,7 @@ void ShardServerOpObserver::onUpdate(OperationContext* opCtx, const OplogUpdateE // block. AllowLockAcquisitionOnTimestampedUnitOfWork allowLockAcquisition(opCtx->lockState()); AutoGetDb autoDb(opCtx, db, MODE_X); - auto dss = DatabaseShardingState::get(opCtx, db); - dss->clearDatabaseInfo(opCtx); + DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, DatabaseName(boost::none, db)); } } @@ -492,8 +492,7 @@ 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, deletedDatabase, MODE_X); - auto dss = DatabaseShardingState::get(opCtx, deletedDatabase); - dss->clearDatabaseInfo(opCtx); + DatabaseHolder::get(opCtx)->clearDbInfo(opCtx, DatabaseName(boost::none, deletedDatabase)); } if (nss == NamespaceString::kServerConfigurationNamespace) { diff --git a/src/mongo/db/s/sharding_ddl_coordinator.cpp b/src/mongo/db/s/sharding_ddl_coordinator.cpp index 4b556f55231..e5441b7e2ec 100644 --- a/src/mongo/db/s/sharding_ddl_coordinator.cpp +++ b/src/mongo/db/s/sharding_ddl_coordinator.cpp @@ -32,6 +32,7 @@ #include "mongo/db/s/sharding_ddl_coordinator.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/repl/repl_client_info.h" @@ -270,7 +271,7 @@ SemiFuture<void> ShardingDDLCoordinator::run(std::shared_ptr<executor::ScopedTas metadata().getDatabaseVersion() /* databaseVersion */); // Check under the dbLock if this is still the primary shard for the database - DatabaseShardingState::checkIsPrimaryShardForDb(opCtx, originalNss().db()); + catalog_helper::assertIsPrimaryShardForDb(opCtx, originalNss().db()); }; }) .then([this, executor, token, anchor = shared_from_this()] { diff --git a/src/mongo/db/s/sharding_ddl_coordinator_service.cpp b/src/mongo/db/s/sharding_ddl_coordinator_service.cpp index 42bf4aced00..101846166a0 100644 --- a/src/mongo/db/s/sharding_ddl_coordinator_service.cpp +++ b/src/mongo/db/s/sharding_ddl_coordinator_service.cpp @@ -33,6 +33,7 @@ #include "mongo/db/s/sharding_ddl_coordinator_service.h" #include "mongo/base/checked_cast.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/pipeline/aggregate_command_gen.h" #include "mongo/db/pipeline/document_source_count.h" @@ -264,7 +265,7 @@ ShardingDDLCoordinatorService::getOrCreateInstance(OperationContext* opCtx, BSON uassert(ErrorCodes::IllegalOperation, "Request sent without attaching database version", clientDbVersion); - DatabaseShardingState::checkIsPrimaryShardForDb(opCtx, nss.db()); + catalog_helper::assertIsPrimaryShardForDb(opCtx, nss.db()); coorMetadata.setDatabaseVersion(clientDbVersion); } diff --git a/src/mongo/db/s/shardsvr_drop_indexes_command.cpp b/src/mongo/db/s/shardsvr_drop_indexes_command.cpp index acbb42422b9..2fe7075be30 100644 --- a/src/mongo/db/s/shardsvr_drop_indexes_command.cpp +++ b/src/mongo/db/s/shardsvr_drop_indexes_command.cpp @@ -29,6 +29,7 @@ #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/catalog_helper.h" #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" @@ -159,7 +160,7 @@ ShardsvrDropIndexesCommand::Invocation::Response ShardsvrDropIndexesCommand::Inv auto dbLocalLock = distLockManager->lockDirectLocally(opCtx, ns().db(), lockTimeout); // Check under the dbLock if this is still the primary shard for the database - DatabaseShardingState::checkIsPrimaryShardForDb(opCtx, ns().db()); + catalog_helper::assertIsPrimaryShardForDb(opCtx, ns().db()); auto resolvedNs = ns(); auto dropIdxBSON = dropIdxCmd.toBSON({}); |