diff options
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency.h | 1 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_manager_defs.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_manager_defs.h | 57 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_stats.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_stats.h | 27 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker.h | 33 |
8 files changed, 142 insertions, 31 deletions
diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp index db11862b65b..d63ac182dc1 100644 --- a/src/mongo/db/concurrency/d_concurrency.cpp +++ b/src/mongo/db/concurrency/d_concurrency.cpp @@ -133,6 +133,7 @@ Lock::GlobalLock::GlobalLock(OperationContext* opCtx, : _opCtx(opCtx), _result(LOCK_INVALID), _pbwm(opCtx->lockState(), resourceIdParallelBatchWriterMode), + _fcvLock(opCtx->lockState(), resourceIdFeatureCompatibilityVersion), _interruptBehavior(behavior), _skipRSTLLock(skipRSTLLock), _isOutermostLock(!opCtx->lockState()->isLocked()) { @@ -148,6 +149,17 @@ Lock::GlobalLock::GlobalLock(OperationContext* opCtx, } }); + if (_opCtx->lockState()->shouldConflictWithSetFeatureCompatibilityVersion() && + !isSharedLockMode(lockMode)) { + _fcvLock.lock(_opCtx, MODE_IX, deadline); + } + ScopeGuard unlockFCVLock([this, lockMode] { + if (_opCtx->lockState()->shouldConflictWithSetFeatureCompatibilityVersion() && + !isSharedLockMode(lockMode)) { + _fcvLock.unlock(); + } + }); + _result = LOCK_INVALID; if (skipRSTLLock) { _takeGlobalLockOnly(lockMode, deadline); @@ -156,6 +168,7 @@ Lock::GlobalLock::GlobalLock(OperationContext* opCtx, } _result = LOCK_OK; + unlockFCVLock.dismiss(); unlockPBWM.dismiss(); } catch (const ExceptionForCat<ErrorCategory::Interruption>&) { // The kLeaveUnlocked behavior suppresses this exception. @@ -184,6 +197,7 @@ Lock::GlobalLock::GlobalLock(GlobalLock&& otherLock) : _opCtx(otherLock._opCtx), _result(otherLock._result), _pbwm(std::move(otherLock._pbwm)), + _fcvLock(std::move(otherLock._fcvLock)), _interruptBehavior(otherLock._interruptBehavior), _skipRSTLLock(otherLock._skipRSTLLock), _isOutermostLock(otherLock._isOutermostLock) { diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h index 1c21e9362af..4e3433d5f6d 100644 --- a/src/mongo/db/concurrency/d_concurrency.h +++ b/src/mongo/db/concurrency/d_concurrency.h @@ -252,6 +252,7 @@ public: OperationContext* const _opCtx; LockResult _result; ResourceLock _pbwm; + ResourceLock _fcvLock; InterruptBehavior _interruptBehavior; bool _skipRSTLLock; const bool _isOutermostLock; diff --git a/src/mongo/db/concurrency/lock_manager_defs.cpp b/src/mongo/db/concurrency/lock_manager_defs.cpp index 01ae4586cad..bc9c357cc01 100644 --- a/src/mongo/db/concurrency/lock_manager_defs.cpp +++ b/src/mongo/db/concurrency/lock_manager_defs.cpp @@ -35,8 +35,13 @@ namespace mongo { const ResourceId resourceIdLocalDB = ResourceId(RESOURCE_DATABASE, StringData("local")); const ResourceId resourceIdOplog = ResourceId(RESOURCE_COLLECTION, StringData("local.oplog.rs")); const ResourceId resourceIdAdminDB = ResourceId(RESOURCE_DATABASE, StringData("admin")); -const ResourceId resourceIdGlobal = ResourceId(RESOURCE_GLOBAL, 1ULL); -const ResourceId resourceIdParallelBatchWriterMode = ResourceId(RESOURCE_PBWM, 1ULL); -const ResourceId resourceIdReplicationStateTransitionLock = ResourceId(RESOURCE_RSTL, 1ULL); +const ResourceId resourceIdGlobal = + ResourceId(RESOURCE_GLOBAL, static_cast<uint8_t>(ResourceGlobalId::kGlobal)); +const ResourceId resourceIdParallelBatchWriterMode = + ResourceId(RESOURCE_GLOBAL, static_cast<uint8_t>(ResourceGlobalId::kParallelBatchWriterMode)); +const ResourceId resourceIdFeatureCompatibilityVersion = ResourceId( + RESOURCE_GLOBAL, static_cast<uint8_t>(ResourceGlobalId::kFeatureCompatibilityVersion)); +const ResourceId resourceIdReplicationStateTransitionLock = ResourceId( + RESOURCE_GLOBAL, static_cast<uint8_t>(ResourceGlobalId::kReplicationStateTransitionLock)); } // namespace mongo diff --git a/src/mongo/db/concurrency/lock_manager_defs.h b/src/mongo/db/concurrency/lock_manager_defs.h index 857599e8cb8..d2fe7a1df62 100644 --- a/src/mongo/db/concurrency/lock_manager_defs.h +++ b/src/mongo/db/concurrency/lock_manager_defs.h @@ -155,12 +155,6 @@ enum LockResult { enum ResourceType { RESOURCE_INVALID = 0, - /** Parallel batch writer mode lock */ - RESOURCE_PBWM, - - /** Replication state transition lock. */ - RESOURCE_RSTL, - /** Used for global exclusive operations */ RESOURCE_GLOBAL, @@ -177,21 +171,42 @@ enum ResourceType { }; /** + * IDs for usages of RESOURCE_GLOBAL. + */ +enum class ResourceGlobalId : uint8_t { + kParallelBatchWriterMode, + kFeatureCompatibilityVersion, + kReplicationStateTransitionLock, + kGlobal, + + // The number of global resource ids. Always insert new ids above this entry. + kNumIds +}; + +/** * Maps the resource id to a human-readable string. */ -static const char* ResourceTypeNames[] = {"Invalid", - "ParallelBatchWriterMode", - "ReplicationStateTransition", - "Global", - "Database", - "Collection", - "Metadata", - "Mutex"}; +static const char* ResourceTypeNames[] = { + "Invalid", "Global", "Database", "Collection", "Metadata", "Mutex"}; + +/** + * Maps the global resource id to a human-readable string. + */ +static const char* ResourceGlobalIdNames[] = { + "ParallelBatchWriterMode", + "FeatureCompatibilityVersion", + "ReplicationStateTransition", + "Global", +}; // Ensure we do not add new types without updating the names array. MONGO_STATIC_ASSERT((sizeof(ResourceTypeNames) / sizeof(ResourceTypeNames[0])) == ResourceTypesCount); +// Ensure we do not add new global resource ids without updating the names array. +MONGO_STATIC_ASSERT((sizeof(ResourceGlobalIdNames) / sizeof(ResourceGlobalIdNames[0])) == + static_cast<uint8_t>(ResourceGlobalId::kNumIds)); + /** * Returns a human-readable name for the specified resource type. */ @@ -200,6 +215,13 @@ static const char* resourceTypeName(ResourceType resourceType) { } /** + * Returns a human-readable name for the specified global resource. + */ +static const char* resourceGlobalIdName(ResourceGlobalId id) { + return ResourceGlobalIdNames[static_cast<uint8_t>(id)]; +} + +/** * Uniquely identifies a lockable resource. */ class ResourceId { @@ -291,9 +313,14 @@ extern const ResourceId resourceIdGlobal; // this lock. extern const ResourceId resourceIdParallelBatchWriterMode; +// Hardcoded resource id for a full FCV transition from start -> upgrading -> upgraded (or +// equivalent for downgrading). This lock is used as a barrier to prevent writes from spanning an +// FCV change. This lock is acquired after the PBWM but before the RSTL and resourceIdGlobal. +extern const ResourceId resourceIdFeatureCompatibilityVersion; + // Hardcoded resource id for the ReplicationStateTransitionLock (RSTL). This lock is acquired in // mode X for any replication state transition and is acquired by all other reads and writes in mode -// IX. This lock is acquired after the PBWM but before the resourceIdGlobal. +// IX. This lock is acquired after the PBWM and FCV locks but before the resourceIdGlobal. extern const ResourceId resourceIdReplicationStateTransitionLock; /** diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp index 33567b900b8..fa7dd845bb4 100644 --- a/src/mongo/db/concurrency/lock_state.cpp +++ b/src/mongo/db/concurrency/lock_state.cpp @@ -144,8 +144,6 @@ bool LockerImpl::_shouldDelayUnlock(ResourceId resId, LockMode mode) const { return false; case RESOURCE_GLOBAL: - case RESOURCE_PBWM: - case RESOURCE_RSTL: case RESOURCE_DATABASE: case RESOURCE_COLLECTION: case RESOURCE_METADATA: @@ -437,8 +435,7 @@ bool LockerImpl::unlockGlobal() { // error for any lock used with multi-granularity locking to have more references than // the global lock, because every scope starts by calling lockGlobal. const auto resType = it.key().getType(); - if (resType == RESOURCE_GLOBAL || resType == RESOURCE_PBWM || resType == RESOURCE_RSTL || - resType == RESOURCE_MUTEX) { + if (resType == RESOURCE_GLOBAL || resType == RESOURCE_MUTEX) { it.next(); } else { invariant(_unlockImpl(&it)); @@ -789,8 +786,9 @@ bool LockerImpl::saveLockStateAndUnlock(Locker::LockSnapshot* stateOut) { // We should never have to save and restore metadata locks. invariant(RESOURCE_DATABASE == resType || RESOURCE_COLLECTION == resType || - (RESOURCE_PBWM == resType && isSharedLockMode(it->mode)) || - (RESOURCE_RSTL == resType && it->mode == MODE_IX)); + (resId == resourceIdParallelBatchWriterMode && isSharedLockMode(it->mode)) || + resId == resourceIdFeatureCompatibilityVersion || + (resId == resourceIdReplicationStateTransitionLock && it->mode == MODE_IX)); // And, stuff the info into the out parameter. OneLock info; @@ -890,7 +888,7 @@ LockResult LockerImpl::_lockBegin(OperationContext* opCtx, ResourceId resId, Loc // Give priority to the full modes for Global, PBWM, and RSTL resources so we don't stall global // operations such as shutdown or stepdown. const ResourceType resType = resId.getType(); - if (resType == RESOURCE_GLOBAL || resType == RESOURCE_PBWM || resType == RESOURCE_RSTL) { + if (resType == RESOURCE_GLOBAL) { if (mode == MODE_S || mode == MODE_X) { request->enqueueAtFront = true; request->compatibleFirst = true; diff --git a/src/mongo/db/concurrency/lock_stats.cpp b/src/mongo/db/concurrency/lock_stats.cpp index f724c90a240..980b4fd0a5e 100644 --- a/src/mongo/db/concurrency/lock_stats.cpp +++ b/src/mongo/db/concurrency/lock_stats.cpp @@ -42,9 +42,15 @@ LockStats<CounterType>::LockStats() { template <typename CounterType> void LockStats<CounterType>::report(BSONObjBuilder* builder) const { - // All indexing below starts from offset 1, because we do not want to report/account - // position 0, which is a sentinel value for invalid resource/no lock. - for (int i = 1; i < ResourceTypesCount; i++) { + for (uint8_t i = 0; i < static_cast<uint8_t>(ResourceGlobalId::kNumIds); ++i) { + _report(builder, + resourceGlobalIdName(static_cast<ResourceGlobalId>(i)), + _resourceGlobalStats[i]); + } + + // Index starting from offset 2 because position 0 is a sentinel value for invalid resource/no + // lock, and position 1 is the global resource which was already reported above. + for (int i = 2; i < ResourceTypesCount; i++) { _report(builder, resourceTypeName(static_cast<ResourceType>(i)), _stats[i]); } @@ -119,6 +125,12 @@ void LockStats<CounterType>::_report(BSONObjBuilder* builder, template <typename CounterType> void LockStats<CounterType>::reset() { + for (uint8_t i = 0; i < static_cast<uint8_t>(ResourceGlobalId::kNumIds); ++i) { + for (uint8_t mode = 0; mode < LockModesCount; ++mode) { + _resourceGlobalStats[i].modeStats[mode].reset(); + } + } + for (int i = 0; i < ResourceTypesCount; i++) { for (int mode = 0; mode < LockModesCount; mode++) { _stats[i].modeStats[mode].reset(); diff --git a/src/mongo/db/concurrency/lock_stats.h b/src/mongo/db/concurrency/lock_stats.h index d45b3b38864..69855480393 100644 --- a/src/mongo/db/concurrency/lock_stats.h +++ b/src/mongo/db/concurrency/lock_stats.h @@ -141,6 +141,10 @@ public: return _oplogStats.modeStats[mode]; } + if (resId.getType() == RESOURCE_GLOBAL) { + return _resourceGlobalStats[resId.getHashId()].modeStats[mode]; + } + return _stats[resId.getType()].modeStats[mode]; } @@ -148,7 +152,15 @@ public: void append(const LockStats<OtherType>& other) { typedef LockStatCounters<OtherType> OtherLockStatCountersType; - // Append all lock stats + // Append global lock stats. + for (uint8_t i = 0; i < static_cast<uint8_t>(ResourceGlobalId::kNumIds); ++i) { + for (uint8_t mode = 0; mode < LockModesCount; ++mode) { + _resourceGlobalStats[i].modeStats[mode].append( + other._resourceGlobalStats[i].modeStats[mode]); + } + } + + // Append all non-global, non-oplog lock stats. for (int i = 0; i < ResourceTypesCount; i++) { for (int mode = 0; mode < LockModesCount; mode++) { const OtherLockStatCountersType& otherStats = other._stats[i].modeStats[mode]; @@ -169,6 +181,13 @@ public: void subtract(const LockStats<OtherType>& other) { typedef LockStatCounters<OtherType> OtherLockStatCountersType; + for (uint8_t i = 0; i < static_cast<uint8_t>(ResourceGlobalId::kNumIds); ++i) { + for (uint8_t mode = 0; mode < LockModesCount; ++mode) { + _resourceGlobalStats[i].modeStats[mode].subtract( + other._resourceGlobalStats[i].modeStats[mode]); + } + } + for (int i = 0; i < ResourceTypesCount; i++) { for (int mode = 0; mode < LockModesCount; mode++) { const OtherLockStatCountersType& otherStats = other._stats[i].modeStats[mode]; @@ -206,8 +225,10 @@ private: const PerModeLockStatCounters& stat) const; - // Split the lock stats per resource type. Special-case the oplog so we can collect more - // detailed stats for it. + // For the global resource, split the lock stats per ID since each one should be reported + // separately. For the remaining resources, split the lock stats per resource type. Special-case + // the oplog so we can collect more detailed stats for it. + PerModeLockStatCounters _resourceGlobalStats[static_cast<uint8_t>(ResourceGlobalId::kNumIds)]; PerModeLockStatCounters _stats[ResourceTypesCount]; PerModeLockStatCounters _oplogStats; }; diff --git a/src/mongo/db/concurrency/locker.h b/src/mongo/db/concurrency/locker.h index 37b6dbeedb8..5945ab1857b 100644 --- a/src/mongo/db/concurrency/locker.h +++ b/src/mongo/db/concurrency/locker.h @@ -494,6 +494,18 @@ public: } /** + * If set to false, this opts out of conflicting with the barrier created by the + * setFeatureCompatibilityVersion command. Code that opts-out must be ok with writes being able + * to start under one FCV and complete under a different FCV. + */ + void setShouldConflictWithSetFeatureCompatibilityVersion(bool newValue) { + _shouldConflictWithSetFeatureCompatibilityVersion = newValue; + } + bool shouldConflictWithSetFeatureCompatibilityVersion() const { + return _shouldConflictWithSetFeatureCompatibilityVersion; + } + + /** * If set to true, this opts out of a fatal assertion where operations which are holding open an * oplog hole cannot try to acquire subsequent locks. */ @@ -570,6 +582,7 @@ protected: private: bool _shouldConflictWithSecondaryBatchApplication = true; + bool _shouldConflictWithSetFeatureCompatibilityVersion = true; bool _shouldAllowLockAcquisitionOnTimestampedUnitOfWork = false; bool _shouldAcquireTicket = true; std::string _debugInfo; // Extra info about this locker for debugging purpose @@ -634,6 +647,26 @@ private: }; /** + * RAII-style class to opt out the FeatureCompatibilityVersion lock. + */ +class ShouldNotConflictWithSetFeatureCompatibilityVersionBlock { +public: + explicit ShouldNotConflictWithSetFeatureCompatibilityVersionBlock(Locker* lockState) + : _lockState(lockState), + _originalShouldConflict(_lockState->shouldConflictWithSetFeatureCompatibilityVersion()) { + _lockState->setShouldConflictWithSetFeatureCompatibilityVersion(false); + } + + ~ShouldNotConflictWithSetFeatureCompatibilityVersionBlock() { + _lockState->setShouldConflictWithSetFeatureCompatibilityVersion(_originalShouldConflict); + } + +private: + Locker* const _lockState; + const bool _originalShouldConflict; +}; + +/** * RAII-style class to opt out of a fatal assertion where operations that set a timestamp on a * WriteUnitOfWork cannot try to acquire subsequent locks. When an operation is writing at a * specific timestamp, it creates an oplog hole at that timestamp. The oplog visibility rules only |