diff options
19 files changed, 223 insertions, 115 deletions
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index a6b71b91e7d..cb32f5c9349 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -157,7 +157,9 @@ Status renameCollectionCommon(OperationContext* opCtx, auto databaseHolder = DatabaseHolder::get(opCtx); auto sourceDB = databaseHolder->getDb(opCtx, source.db()); if (sourceDB) { - DatabaseShardingState::get(sourceDB).checkDbVersion(opCtx); + auto& dss = DatabaseShardingState::get(sourceDB); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + dss.checkDbVersion(opCtx, dssLock); } Collection* const sourceColl = sourceDB ? sourceDB->getCollection(opCtx, source) : nullptr; if (!sourceColl) { diff --git a/src/mongo/db/catalog_raii.cpp b/src/mongo/db/catalog_raii.cpp index 342c4d60a61..fdefc58956a 100644 --- a/src/mongo/db/catalog_raii.cpp +++ b/src/mongo/db/catalog_raii.cpp @@ -59,7 +59,9 @@ AutoGetDb::AutoGetDb(OperationContext* opCtx, StringData dbName, LockMode mode, return databaseHolder->getDb(opCtx, dbName); }()) { if (_db) { - DatabaseShardingState::get(_db).checkDbVersion(opCtx); + auto& dss = DatabaseShardingState::get(_db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + dss.checkDbVersion(opCtx, dssLock); } } @@ -171,7 +173,9 @@ AutoGetOrCreateDb::AutoGetOrCreateDb(OperationContext* opCtx, _db = databaseHolder->openDb(opCtx, dbName, &_justCreated); } - DatabaseShardingState::get(_db).checkDbVersion(opCtx); + auto& dss = DatabaseShardingState::get(_db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + dss.checkDbVersion(opCtx, dssLock); } ConcealUUIDCatalogChangesBlock::ConcealUUIDCatalogChangesBlock(OperationContext* opCtx) diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index f3238ab69a1..040870f1f97 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -251,7 +251,12 @@ bool runCreateIndexes(OperationContext* opCtx, if (!db) { db = databaseHolder->openDb(opCtx, ns.db()); } - DatabaseShardingState::get(db).checkDbVersion(opCtx); + + { + auto& dss = DatabaseShardingState::get(db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + dss.checkDbVersion(opCtx, dssLock); + } Collection* collection = db->getCollection(opCtx, ns); if (collection) { @@ -380,7 +385,9 @@ bool runCreateIndexes(OperationContext* opCtx, auto db = databaseHolder->getDb(opCtx, ns.db()); if (db) { - DatabaseShardingState::get(db).checkDbVersion(opCtx); + auto& dss = DatabaseShardingState::get(db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + dss.checkDbVersion(opCtx, dssLock); } invariant(db); diff --git a/src/mongo/db/index_builds_coordinator.cpp b/src/mongo/db/index_builds_coordinator.cpp index c610e4f9c0d..e77d4b1e046 100644 --- a/src/mongo/db/index_builds_coordinator.cpp +++ b/src/mongo/db/index_builds_coordinator.cpp @@ -536,7 +536,9 @@ void IndexBuildsCoordinator::_runIndexBuild(OperationContext* opCtx, auto databaseHolder = DatabaseHolder::get(opCtx); auto db = databaseHolder->getDb(opCtx, nss.db()); if (db) { - DatabaseShardingState::get(db).checkDbVersion(opCtx); + auto& dss = DatabaseShardingState::get(db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + dss.checkDbVersion(opCtx, dssLock); } invariant(db, diff --git a/src/mongo/db/s/active_migrations_registry.cpp b/src/mongo/db/s/active_migrations_registry.cpp index 6f12e0119ec..d070142ec16 100644 --- a/src/mongo/db/s/active_migrations_registry.cpp +++ b/src/mongo/db/s/active_migrations_registry.cpp @@ -122,7 +122,7 @@ BSONObj ActiveMigrationsRegistry::getActiveMigrationStatusReport(OperationContex // Lock the collection so nothing changes while we're getting the migration report. AutoGetCollection autoColl(opCtx, nss.get(), MODE_IS); auto csr = CollectionShardingRuntime::get(opCtx, nss.get()); - auto csrLock = CollectionShardingRuntimeLock::lock(opCtx, csr); + auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); if (auto msm = MigrationSourceManager::get(csr, csrLock)) { return msm->getMigrationStatusReport(); diff --git a/src/mongo/db/s/collection_sharding_runtime.cpp b/src/mongo/db/s/collection_sharding_runtime.cpp index 2eff2a8f609..2990e877dd2 100644 --- a/src/mongo/db/s/collection_sharding_runtime.cpp +++ b/src/mongo/db/s/collection_sharding_runtime.cpp @@ -185,26 +185,6 @@ boost::optional<ScopedCollectionMetadata> CollectionShardingRuntime::_getMetadat return _metadataManager->getActiveMetadata(_metadataManager, atClusterTime); } -CollectionShardingRuntimeLock::CollectionShardingRuntimeLock(OperationContext* opCtx, - CollectionShardingRuntime* csr, - LockMode lockMode) - : _lock([&]() -> CSRLock { - invariant(lockMode == MODE_IS || lockMode == MODE_X); - return (lockMode == MODE_IS - ? CSRLock(Lock::SharedLock(opCtx->lockState(), csr->_stateChangeMutex)) - : CSRLock(Lock::ExclusiveLock(opCtx->lockState(), csr->_stateChangeMutex))); - }()) {} - -CollectionShardingRuntimeLock CollectionShardingRuntimeLock::lock(OperationContext* opCtx, - CollectionShardingRuntime* csr) { - return CollectionShardingRuntimeLock(opCtx, csr, MODE_IS); -} - -CollectionShardingRuntimeLock CollectionShardingRuntimeLock::lockExclusive( - OperationContext* opCtx, CollectionShardingRuntime* csr) { - return CollectionShardingRuntimeLock(opCtx, csr, MODE_X); -} - CollectionCriticalSection::CollectionCriticalSection(OperationContext* opCtx, NamespaceString ns) : _nss(std::move(ns)), _opCtx(opCtx) { AutoGetCollection autoColl(_opCtx, diff --git a/src/mongo/db/s/collection_sharding_runtime.h b/src/mongo/db/s/collection_sharding_runtime.h index 89171876643..1cd77d7fde6 100644 --- a/src/mongo/db/s/collection_sharding_runtime.h +++ b/src/mongo/db/s/collection_sharding_runtime.h @@ -35,14 +35,13 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/s/collection_sharding_state.h" #include "mongo/db/s/metadata_manager.h" +#include "mongo/db/s/sharding_state_lock.h" #include "mongo/platform/atomic_word.h" #include "mongo/stdx/variant.h" #include "mongo/util/decorable.h" namespace mongo { -class CollectionShardingRuntimeLock; - extern AtomicWord<int> migrationLockAcquisitionMaxWaitMS; /** @@ -54,6 +53,8 @@ class CollectionShardingRuntime final : public CollectionShardingState, MONGO_DISALLOW_COPYING(CollectionShardingRuntime); public: + using CSRLock = ShardingStateLock<CollectionShardingRuntime>; + CollectionShardingRuntime(ServiceContext* sc, NamespaceString nss, executor::TaskExecutor* rangeDeleterExecutor); @@ -149,7 +150,7 @@ public: } private: - friend CollectionShardingRuntimeLock; + friend CSRLock; friend boost::optional<Date_t> CollectionRangeDeleter::cleanUpNextRange( OperationContext*, NamespaceString const&, OID const&, int, CollectionRangeDeleter*); @@ -169,42 +170,6 @@ private: }; /** - * RAII-style class that locks the CollectionShardingRuntime using the CollectionShardingRuntime's - * ResourceMutex. The lock will be created and acquired on construction. The lock will be dismissed - * upon destruction of the CollectionShardingRuntimeLock object. - */ -class CollectionShardingRuntimeLock { - -public: - using CSRLock = stdx::variant<Lock::SharedLock, Lock::ExclusiveLock>; - - /** - * Locks the sharding runtime state for the specified collection with the - * CollectionShardingRuntime object's ResourceMutex in MODE_IS. When the object goes out of - * scope, the ResourceMutex will be unlocked. - */ - static CollectionShardingRuntimeLock lock(OperationContext* opCtx, - CollectionShardingRuntime* csr); - - /** - * Follows the same functionality as the CollectionShardingRuntimeLock lock method, except - * that lockExclusive takes the ResourceMutex in MODE_X. - */ - static CollectionShardingRuntimeLock lockExclusive(OperationContext* opCtx, - CollectionShardingRuntime* csr); - -private: - CollectionShardingRuntimeLock(OperationContext* opCtx, - CollectionShardingRuntime* csr, - LockMode lockMode); - - // The lock created and locked upon construction of a CollectionShardingRuntimeLock object. - // It locks the ResourceMutex taken from the CollectionShardingRuntime class, passed in on - // construction. - CSRLock _lock; -}; - -/** * RAII-style class, which obtains a reference to the critical section for the specified collection. */ class CollectionCriticalSection { diff --git a/src/mongo/db/s/database_sharding_state.cpp b/src/mongo/db/s/database_sharding_state.cpp index 3c8d81010a5..37893af7df6 100644 --- a/src/mongo/db/s/database_sharding_state.cpp +++ b/src/mongo/db/s/database_sharding_state.cpp @@ -48,25 +48,26 @@ const Database::Decoration<DatabaseShardingState> DatabaseShardingState::get = DatabaseShardingState::DatabaseShardingState() = default; -void DatabaseShardingState::enterCriticalSectionCatchUpPhase(OperationContext* opCtx) { +void DatabaseShardingState::enterCriticalSectionCatchUpPhase(OperationContext* opCtx, DSSLock&) { invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)); _critSec.enterCriticalSectionCatchUpPhase(); } -void DatabaseShardingState::enterCriticalSectionCommitPhase(OperationContext* opCtx) { +void DatabaseShardingState::enterCriticalSectionCommitPhase(OperationContext* opCtx, DSSLock&) { invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)); _critSec.enterCriticalSectionCommitPhase(); } void DatabaseShardingState::exitCriticalSection(OperationContext* opCtx, - boost::optional<DatabaseVersion> newDbVersion) { - invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)); + boost::optional<DatabaseVersion> newDbVersion, + DSSLock&) { + invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_IX)); _critSec.exitCriticalSection(); _dbVersion = newDbVersion; } -boost::optional<DatabaseVersion> DatabaseShardingState::getDbVersion( - OperationContext* opCtx) const { +boost::optional<DatabaseVersion> DatabaseShardingState::getDbVersion(OperationContext* opCtx, + DSSLock&) const { if (!opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)) { invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_IS)); } @@ -74,14 +75,15 @@ boost::optional<DatabaseVersion> DatabaseShardingState::getDbVersion( } void DatabaseShardingState::setDbVersion(OperationContext* opCtx, - boost::optional<DatabaseVersion> newDbVersion) { + boost::optional<DatabaseVersion> newDbVersion, + DSSLock&) { invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)); log() << "setting this node's cached database version for " << get.owner(this)->name() << " to " << (newDbVersion ? newDbVersion->toBSON() : BSONObj()); _dbVersion = newDbVersion; } -void DatabaseShardingState::checkDbVersion(OperationContext* opCtx) const { +void DatabaseShardingState::checkDbVersion(OperationContext* opCtx, DSSLock&) const { invariant(opCtx->lockState()->isLocked()); const auto dbName = get.owner(this)->name(); @@ -109,12 +111,13 @@ void DatabaseShardingState::checkDbVersion(OperationContext* opCtx) const { databaseVersion::equal(*clientDbVersion, *_dbVersion)); } -MovePrimarySourceManager* DatabaseShardingState::getMovePrimarySourceManager() { +MovePrimarySourceManager* DatabaseShardingState::getMovePrimarySourceManager(DSSLock&) { return _sourceMgr; } void DatabaseShardingState::setMovePrimarySourceManager(OperationContext* opCtx, - MovePrimarySourceManager* sourceMgr) { + MovePrimarySourceManager* sourceMgr, + DSSLock&) { invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)); invariant(sourceMgr); invariant(!_sourceMgr); @@ -122,8 +125,8 @@ void DatabaseShardingState::setMovePrimarySourceManager(OperationContext* opCtx, _sourceMgr = sourceMgr; } -void DatabaseShardingState::clearMovePrimarySourceManager(OperationContext* opCtx) { - invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_X)); +void DatabaseShardingState::clearMovePrimarySourceManager(OperationContext* opCtx, DSSLock&) { + invariant(opCtx->lockState()->isDbLockedForMode(get.owner(this)->name(), MODE_IX)); _sourceMgr = nullptr; } diff --git a/src/mongo/db/s/database_sharding_state.h b/src/mongo/db/s/database_sharding_state.h index 3033fb27ea4..f00157183f0 100644 --- a/src/mongo/db/s/database_sharding_state.h +++ b/src/mongo/db/s/database_sharding_state.h @@ -33,6 +33,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/db/catalog/database.h" #include "mongo/db/s/sharding_migration_critical_section.h" +#include "mongo/db/s/sharding_state_lock.h" #include "mongo/s/database_version_gen.h" namespace mongo { @@ -47,6 +48,12 @@ class DatabaseShardingState { MONGO_DISALLOW_COPYING(DatabaseShardingState); public: + /** + * A ShardingStateLock is used on DatabaseShardingState operations in order to ensure + * synchronization across operations. + */ + using DSSLock = ShardingStateLock<DatabaseShardingState>; + static const Database::Decoration<DatabaseShardingState> get; DatabaseShardingState(); @@ -56,12 +63,13 @@ public: * Methods to control the databases's critical section. Must be called with the database X lock * held. */ - void enterCriticalSectionCatchUpPhase(OperationContext* opCtx); - void enterCriticalSectionCommitPhase(OperationContext* opCtx); + void enterCriticalSectionCatchUpPhase(OperationContext* opCtx, DSSLock&); + void enterCriticalSectionCommitPhase(OperationContext* opCtx, DSSLock&); void exitCriticalSection(OperationContext* opCtx, - boost::optional<DatabaseVersion> newDbVersion); + boost::optional<DatabaseVersion> newDbVersion, + DSSLock&); - auto getCriticalSectionSignal(ShardingMigrationCriticalSection::Operation op) const { + auto getCriticalSectionSignal(ShardingMigrationCriticalSection::Operation op, DSSLock&) const { return _critSec.getSignal(op); } @@ -70,42 +78,52 @@ public: * * Invariants that the caller holds the DBLock in X or IS. */ - boost::optional<DatabaseVersion> getDbVersion(OperationContext* opCtx) const; + boost::optional<DatabaseVersion> getDbVersion(OperationContext* opCtx, DSSLock&) const; /** * Sets this shard server's cached dbVersion to newVersion. * * Invariants that the caller holds the DBLock in X mode. */ - void setDbVersion(OperationContext* opCtx, boost::optional<DatabaseVersion> newVersion); + void setDbVersion(OperationContext* opCtx, + boost::optional<DatabaseVersion> newVersion, + DSSLock&); /** * 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) const; + void checkDbVersion(OperationContext* opCtx, DSSLock&) const; /** * Returns the active movePrimary source manager, if one is available. */ - MovePrimarySourceManager* getMovePrimarySourceManager(); + MovePrimarySourceManager* getMovePrimarySourceManager(DSSLock&); /** * Attaches a movePrimary source manager to this database's sharding state. Must be called with * the database lock in X mode. May not be called if there is a movePrimary source manager * already installed. Must be followed by a call to clearMovePrimarySourceManager. */ - void setMovePrimarySourceManager(OperationContext* opCtx, MovePrimarySourceManager* sourceMgr); + void setMovePrimarySourceManager(OperationContext* opCtx, + MovePrimarySourceManager* sourceMgr, + DSSLock&); /** * Removes a movePrimary source manager from this database's sharding state. Must be called with * with the database lock in X mode. May not be called if there isn't a movePrimary source * manager installed already through a previous call to setMovePrimarySourceManager. */ - void clearMovePrimarySourceManager(OperationContext* opCtx); + void clearMovePrimarySourceManager(OperationContext* opCtx, DSSLock&); private: + friend DSSLock; + + // Object-wide ResourceMutex to protect changes to the DatabaseShardingState or objects held + // within. + Lock::ResourceMutex _stateChangeMutex{"DatabaseShardingState"}; + // Modifying the state below requires holding the DBLock in X mode; holding the DBLock in any // mode is acceptable for reading it. (Note: accessing this class at all requires holding the // DBLock in some mode, since it requires having a pointer to the Database). diff --git a/src/mongo/db/s/flush_database_cache_updates_command.cpp b/src/mongo/db/s/flush_database_cache_updates_command.cpp index 41bbe0dd749..2afcdba3d9d 100644 --- a/src/mongo/db/s/flush_database_cache_updates_command.cpp +++ b/src/mongo/db/s/flush_database_cache_updates_command.cpp @@ -129,10 +129,11 @@ public: // inclusive of the commit (and new writes to the committed chunk) that hasn't yet // propagated back to this shard. This ensures the read your own writes causal // consistency guarantee. - auto criticalSectionSignal = - DatabaseShardingState::get(autoDb.getDb()) - .getCriticalSectionSignal(ShardingMigrationCriticalSection::kRead); - if (criticalSectionSignal) { + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + + if (auto criticalSectionSignal = dss.getCriticalSectionSignal( + ShardingMigrationCriticalSection::kRead, dssLock)) { oss.setMigrationCriticalSectionSignal(criticalSectionSignal); } } diff --git a/src/mongo/db/s/get_database_version_command.cpp b/src/mongo/db/s/get_database_version_command.cpp index 51c7118661c..af4a116e0f1 100644 --- a/src/mongo/db/s/get_database_version_command.cpp +++ b/src/mongo/db/s/get_database_version_command.cpp @@ -80,7 +80,10 @@ public: BSONObj versionObj; AutoGetDb autoDb(opCtx, _targetDb(), MODE_IS); if (auto db = autoDb.getDb()) { - if (auto dbVersion = DatabaseShardingState::get(db).getDbVersion(opCtx)) { + auto& dss = DatabaseShardingState::get(db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + + if (auto dbVersion = dss.getDbVersion(opCtx, dssLock)) { versionObj = dbVersion->toBSON(); } } diff --git a/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp b/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp index 423158365a4..d103d39e144 100644 --- a/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp +++ b/src/mongo/db/s/migration_chunk_cloner_source_legacy_commands.cpp @@ -73,7 +73,7 @@ public: _autoColl->getCollection()); auto csr = CollectionShardingRuntime::get(opCtx, *nss); - _csrLock.emplace(CollectionShardingRuntimeLock::lock(opCtx, csr)); + _csrLock.emplace(CollectionShardingRuntime::CSRLock::lock(opCtx, csr)); if (auto msm = MigrationSourceManager::get(csr, *_csrLock)) { // It is now safe to access the cloner @@ -113,9 +113,9 @@ private: // Scoped database + collection lock boost::optional<AutoGetCollection> _autoColl; - // The CollectionShardingRuntimeLock corresponding to the collection to which this + // The CollectionShardingRuntime::CSRLock corresponding to the collection to which this // migration belongs. - boost::optional<CollectionShardingRuntimeLock> _csrLock; + boost::optional<CollectionShardingRuntime::CSRLock> _csrLock; // Contains the active cloner for the namespace MigrationChunkClonerSourceLegacy* _chunkCloner; diff --git a/src/mongo/db/s/migration_source_manager.cpp b/src/mongo/db/s/migration_source_manager.cpp index 5031490ba21..f3eb6bb825c 100644 --- a/src/mongo/db/s/migration_source_manager.cpp +++ b/src/mongo/db/s/migration_source_manager.cpp @@ -122,7 +122,7 @@ MONGO_FAIL_POINT_DEFINE(hangBeforeLeavingCriticalSection); MONGO_FAIL_POINT_DEFINE(migrationCommitNetworkError); MigrationSourceManager* MigrationSourceManager::get(CollectionShardingRuntime* csr, - CollectionShardingRuntimeLock& csrLock) { + CollectionShardingRuntime::CSRLock& csrLock) { return msmForCsr(csr); } @@ -255,7 +255,7 @@ Status MigrationSourceManager::startClone(OperationContext* opCtx) { opCtx->getServiceContext()->getPreciseClockSource()->now() + Milliseconds(migrationLockAcquisitionMaxWaitMS.load())); auto csr = CollectionShardingRuntime::get(opCtx, getNss()); - auto lockedCsr = CollectionShardingRuntimeLock::lockExclusive(opCtx, csr); + auto lockedCsr = CollectionShardingRuntime::CSRLock::lockExclusive(opCtx, csr); invariant(nullptr == std::exchange(msmForCsr(csr), this)); } diff --git a/src/mongo/db/s/migration_source_manager.h b/src/mongo/db/s/migration_source_manager.h index b2c8546b265..cb6c0d7e7ea 100644 --- a/src/mongo/db/s/migration_source_manager.h +++ b/src/mongo/db/s/migration_source_manager.h @@ -77,7 +77,7 @@ public: * a CollectionShardingRuntime that has its ResourceMutex locked. */ static MigrationSourceManager* get(CollectionShardingRuntime* csr, - CollectionShardingRuntimeLock& csrLock); + CollectionShardingRuntime::CSRLock& csrLock); /** * Instantiates a new migration source manager with the specified migration parameters. Must be diff --git a/src/mongo/db/s/move_primary_source_manager.cpp b/src/mongo/db/s/move_primary_source_manager.cpp index 5b3206c4352..dff9d0a7727 100644 --- a/src/mongo/db/s/move_primary_source_manager.cpp +++ b/src/mongo/db/s/move_primary_source_manager.cpp @@ -89,7 +89,11 @@ Status MovePrimarySourceManager::clone(OperationContext* opCtx) { // We use AutoGetOrCreateDb the first time just in case movePrimary was called before any // data was inserted into the database. AutoGetOrCreateDb autoDb(opCtx, getNss().toString(), MODE_X); - DatabaseShardingState::get(autoDb.getDb()).setMovePrimarySourceManager(opCtx, this); + + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + + dss.setMovePrimarySourceManager(opCtx, this, dssLock); } _state = kCloning; @@ -146,8 +150,11 @@ Status MovePrimarySourceManager::enterCriticalSection(OperationContext* opCtx) { << " was dropped during the movePrimary operation."); } + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + // IMPORTANT: After this line, the critical section is in place and needs to be signaled - DatabaseShardingState::get(autoDb.getDb()).enterCriticalSectionCatchUpPhase(opCtx); + dss.enterCriticalSectionCatchUpPhase(opCtx, dssLock); } _state = kCriticalSection; @@ -195,9 +202,12 @@ Status MovePrimarySourceManager::commitOnConfig(OperationContext* opCtx) { << " was dropped during the movePrimary operation."); } + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + // Read operations must begin to wait on the critical section just before we send the // commit operation to the config server - DatabaseShardingState::get(autoDb.getDb()).enterCriticalSectionCommitPhase(opCtx); + dss.enterCriticalSectionCommitPhase(opCtx, dssLock); } auto configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard(); @@ -244,7 +254,7 @@ Status MovePrimarySourceManager::commitOnConfig(OperationContext* opCtx) { // this node can accept writes for this collection as a proxy for it being primary. if (!validateStatus.isOK()) { UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - AutoGetDb autoDb(opCtx, getNss().toString(), MODE_X); + AutoGetDb autoDb(opCtx, getNss().toString(), MODE_IX); if (!autoDb.getDb()) { uasserted(ErrorCodes::ConflictingOperationInProgress, @@ -253,7 +263,10 @@ Status MovePrimarySourceManager::commitOnConfig(OperationContext* opCtx) { } if (!repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, getNss())) { - DatabaseShardingState::get(autoDb.getDb()).setDbVersion(opCtx, boost::none); + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + + dss.setDbVersion(opCtx, boost::none, dssLock); uassertStatusOK(validateStatus.withContext( str::stream() << "Unable to verify movePrimary commit for database: " << getNss().ns() @@ -345,13 +358,16 @@ void MovePrimarySourceManager::_cleanup(OperationContext* opCtx) { { // Unregister from the database's sharding state if we're still registered. UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - AutoGetDb autoDb(opCtx, getNss().toString(), MODE_X); + AutoGetDb autoDb(opCtx, getNss().toString(), MODE_IX); if (autoDb.getDb()) { - DatabaseShardingState::get(autoDb.getDb()).clearMovePrimarySourceManager(opCtx); + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + + dss.clearMovePrimarySourceManager(opCtx, dssLock); // Leave the critical section if we're still registered. - DatabaseShardingState::get(autoDb.getDb()).exitCriticalSection(opCtx, boost::none); + dss.exitCriticalSection(opCtx, boost::none, dssLock); } } diff --git a/src/mongo/db/s/op_observer_sharding_impl.cpp b/src/mongo/db/s/op_observer_sharding_impl.cpp index 134727d46b9..786aed87d3e 100644 --- a/src/mongo/db/s/op_observer_sharding_impl.cpp +++ b/src/mongo/db/s/op_observer_sharding_impl.cpp @@ -61,7 +61,7 @@ bool OpObserverShardingImpl::isMigrating(OperationContext* opCtx, NamespaceString const& nss, BSONObj const& docToDelete) { auto csr = CollectionShardingRuntime::get(opCtx, nss); - auto csrLock = CollectionShardingRuntimeLock::lock(opCtx, csr); + auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); return msm && msm->getCloner()->isDocumentInMigratingChunk(docToDelete); } @@ -86,7 +86,7 @@ void OpObserverShardingImpl::shardObserveInsertOp(OperationContext* opCtx, { auto csr = CollectionShardingRuntime::get(opCtx, nss); - auto csrLock = CollectionShardingRuntimeLock::lock(opCtx, csr); + auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); if (msm) { @@ -112,7 +112,7 @@ void OpObserverShardingImpl::shardObserveUpdateOp(OperationContext* opCtx, { auto csr = CollectionShardingRuntime::get(opCtx, nss); - auto csrLock = CollectionShardingRuntimeLock::lock(opCtx, csr); + auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); if (msm) { @@ -137,7 +137,7 @@ void OpObserverShardingImpl::shardObserveDeleteOp(OperationContext* opCtx, { auto csr = CollectionShardingRuntime::get(opCtx, nss); - auto csrLock = CollectionShardingRuntimeLock::lock(opCtx, csr); + auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr); auto msm = MigrationSourceManager::get(csr, csrLock); if (msm && isMigrating) { diff --git a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp index 4fd28b98032..f5422a48a38 100644 --- a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp +++ b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp @@ -249,7 +249,10 @@ void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) { return; } - const auto cachedDbVersion = DatabaseShardingState::get(db).getDbVersion(opCtx); + auto& dss = DatabaseShardingState::get(db); + auto dssLock = DatabaseShardingState::DSSLock::lock(opCtx, &dss); + + const auto cachedDbVersion = dss.getDbVersion(opCtx, dssLock); if (cachedDbVersion && cachedDbVersion->getUuid() == refreshedDbVersion.getUuid() && cachedDbVersion->getLastMod() >= refreshedDbVersion.getLastMod()) { LOG(2) << "Skipping setting cached databaseVersion for " << dbName @@ -269,7 +272,10 @@ void forceDatabaseRefresh(OperationContext* opCtx, const StringData dbName) { return; } - DatabaseShardingState::get(db).setDbVersion(opCtx, std::move(refreshedDbVersion)); + auto& dss = DatabaseShardingState::get(db); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + + dss.setDbVersion(opCtx, std::move(refreshedDbVersion), dssLock); } } // 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 c211377f998..863a9f844c7 100644 --- a/src/mongo/db/s/shard_server_op_observer.cpp +++ b/src/mongo/db/s/shard_server_op_observer.cpp @@ -322,7 +322,9 @@ void ShardServerOpObserver::onUpdate(OperationContext* opCtx, const OplogUpdateE if (setField.hasField(ShardDatabaseType::enterCriticalSectionCounter.name())) { AutoGetDb autoDb(opCtx, db, MODE_X); if (autoDb.getDb()) { - DatabaseShardingState::get(autoDb.getDb()).setDbVersion(opCtx, boost::none); + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + dss.setDbVersion(opCtx, boost::none, dssLock); } } } @@ -368,7 +370,9 @@ void ShardServerOpObserver::onDelete(OperationContext* opCtx, AutoGetDb autoDb(opCtx, deletedDatabase, MODE_X); if (autoDb.getDb()) { - DatabaseShardingState::get(autoDb.getDb()).setDbVersion(opCtx, boost::none); + auto& dss = DatabaseShardingState::get(autoDb.getDb()); + auto dssLock = DatabaseShardingState::DSSLock::lockExclusive(opCtx, &dss); + dss.setDbVersion(opCtx, boost::none, dssLock); } } diff --git a/src/mongo/db/s/sharding_state_lock.h b/src/mongo/db/s/sharding_state_lock.h new file mode 100644 index 00000000000..ec2408618ca --- /dev/null +++ b/src/mongo/db/s/sharding_state_lock.h @@ -0,0 +1,97 @@ + +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <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/disallow_copying.h" +#include "mongo/db/concurrency/d_concurrency.h" +#include "mongo/stdx/variant.h" + +namespace mongo { + +/** + * RAII-style class that locks a sharding state object using the state object's ResourceMutex. The + * lock will be created and acquired on construction. The lock will be dismissed upon destruction + * of the sharding state object. + */ +template <class ShardingState> +class ShardingStateLock { + +public: + /** + * Locks the sharding state object with the sharding state object's ResourceMutex in MODE_IS. + * When the object goes out of scope, the ResourceMutex will be unlocked. + */ + static ShardingStateLock<ShardingState> lock(OperationContext* opCtx, ShardingState* state); + + /** + * Follows the same functionality as the ShardingStateLock lock method, except that + * lockExclusive takes the ResourceMutex in MODE_X. + */ + static ShardingStateLock<ShardingState> lockExclusive(OperationContext* opCtx, + ShardingState* state); + +private: + using StateLock = stdx::variant<Lock::SharedLock, Lock::ExclusiveLock>; + + ShardingStateLock<ShardingState>(OperationContext* opCtx, + ShardingState* state, + LockMode lockMode); + + // The lock created and locked upon construction of a ShardingStateLock object. It locks the + // ResourceMutex taken from the ShardingState class, passed in on construction. + StateLock _lock; +}; + +template <class ShardingState> +ShardingStateLock<ShardingState>::ShardingStateLock(OperationContext* opCtx, + ShardingState* state, + LockMode lockMode) + : _lock([&]() -> StateLock { + invariant(lockMode == MODE_IS || lockMode == MODE_X); + return ( + lockMode == MODE_IS + ? StateLock(Lock::SharedLock(opCtx->lockState(), state->_stateChangeMutex)) + : StateLock(Lock::ExclusiveLock(opCtx->lockState(), state->_stateChangeMutex))); + }()) {} + +template <class ShardingState> +ShardingStateLock<ShardingState> ShardingStateLock<ShardingState>::lock(OperationContext* opCtx, + ShardingState* state) { + return ShardingStateLock(opCtx, state, MODE_IS); +} + +template <class ShardingState> +ShardingStateLock<ShardingState> ShardingStateLock<ShardingState>::lockExclusive( + OperationContext* opCtx, ShardingState* state) { + return ShardingStateLock(opCtx, state, MODE_X); +} + +} // namespace mongo
\ No newline at end of file |