summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r--src/mongo/db/concurrency/d_concurrency.cpp14
-rw-r--r--src/mongo/db/concurrency/d_concurrency.h1
-rw-r--r--src/mongo/db/concurrency/lock_manager_defs.cpp11
-rw-r--r--src/mongo/db/concurrency/lock_manager_defs.h57
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp12
-rw-r--r--src/mongo/db/concurrency/lock_stats.cpp18
-rw-r--r--src/mongo/db/concurrency/lock_stats.h27
-rw-r--r--src/mongo/db/concurrency/locker.h33
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