summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/catalog_raii.cpp2
-rw-r--r--src/mongo/db/commands/count_cmd.cpp7
-rw-r--r--src/mongo/db/commands/distinct.cpp4
-rw-r--r--src/mongo/db/commands/map_reduce_agg.cpp2
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp5
-rw-r--r--src/mongo/db/db_raii.cpp53
-rw-r--r--src/mongo/db/db_raii.h7
-rw-r--r--src/mongo/db/pipeline/pipeline_d.cpp3
-rw-r--r--src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp2
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp4
-rw-r--r--src/mongo/dbtests/query_stage_near.cpp2
11 files changed, 60 insertions, 31 deletions
diff --git a/src/mongo/db/catalog_raii.cpp b/src/mongo/db/catalog_raii.cpp
index 841b4a8c43f..e5c2043b152 100644
--- a/src/mongo/db/catalog_raii.cpp
+++ b/src/mongo/db/catalog_raii.cpp
@@ -83,6 +83,8 @@ AutoGetCollection::AutoGetCollection(OperationContext* opCtx,
!nsOrUUID.dbname().empty() ? nsOrUUID.dbname() : nsOrUUID.nss()->db(),
isSharedLockMode(modeColl) ? MODE_IS : MODE_IX,
deadline) {
+ invariant(!opCtx->isLockFreeReadsOp());
+
auto& nss = nsOrUUID.nss();
if (nss) {
uassert(ErrorCodes::InvalidNamespace,
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index be6b252de96..efcc270186f 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -27,6 +27,8 @@
* it in the license file.
*/
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
+
#include "mongo/platform/basic.h"
#include "mongo/db/auth/authorization_session.h"
@@ -46,6 +48,7 @@
#include "mongo/db/query/view_response_formatter.h"
#include "mongo/db/s/collection_sharding_state.h"
#include "mongo/db/views/resolved_view.h"
+#include "mongo/logv2/log.h"
namespace mongo {
namespace {
@@ -136,7 +139,7 @@ public:
const BSONObj& cmdObj = opMsgRequest.body;
// Acquire locks. The RAII object is optional, because in the case
// of a view, the locks need to be released.
- boost::optional<AutoGetCollectionForReadCommand> ctx;
+ boost::optional<AutoGetCollectionForReadCommandMaybeLockFree> ctx;
ctx.emplace(opCtx,
CommandHelpers::parseNsCollectionRequired(dbname, cmdObj),
AutoGetCollectionViewMode::kViewsPermitted);
@@ -212,7 +215,7 @@ public:
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
// Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
// of a view, the locks need to be released.
- boost::optional<AutoGetCollectionForReadCommand> ctx;
+ boost::optional<AutoGetCollectionForReadCommandMaybeLockFree> ctx;
ctx.emplace(opCtx,
CommandHelpers::parseNsOrUUID(dbname, cmdObj),
AutoGetCollectionViewMode::kViewsPermitted);
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 34b8cddab51..70a710ff044 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -138,7 +138,7 @@ public:
const BSONObj& cmdObj = request.body;
// Acquire locks. The RAII object is optional, because in the case of a view, the locks
// need to be released.
- boost::optional<AutoGetCollectionForReadCommand> ctx;
+ boost::optional<AutoGetCollectionForReadCommandMaybeLockFree> ctx;
ctx.emplace(opCtx,
CommandHelpers::parseNsCollectionRequired(dbname, cmdObj),
AutoGetCollectionViewMode::kViewsPermitted);
@@ -195,7 +195,7 @@ public:
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
// Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
// of a view, the locks need to be released.
- boost::optional<AutoGetCollectionForReadCommand> ctx;
+ boost::optional<AutoGetCollectionForReadCommandMaybeLockFree> ctx;
ctx.emplace(opCtx,
CommandHelpers::parseNsOrUUID(dbname, cmdObj),
AutoGetCollectionViewMode::kViewsPermitted);
diff --git a/src/mongo/db/commands/map_reduce_agg.cpp b/src/mongo/db/commands/map_reduce_agg.cpp
index 4e404097d93..a8e48265255 100644
--- a/src/mongo/db/commands/map_reduce_agg.cpp
+++ b/src/mongo/db/commands/map_reduce_agg.cpp
@@ -63,7 +63,7 @@ auto makeExpressionContext(OperationContext* opCtx,
boost::optional<ExplainOptions::Verbosity> verbosity) {
// AutoGetCollectionForReadCommand will throw if the sharding version for this connection is
// out of date.
- AutoGetCollectionForReadCommand ctx(
+ AutoGetCollectionForReadCommandMaybeLockFree ctx(
opCtx, parsedMr.getNamespace(), AutoGetCollectionViewMode::kViewsPermitted);
uassert(ErrorCodes::CommandNotSupportedOnView,
"mapReduce on a view is not supported",
diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp
index 3be2cebb251..e2508806b83 100644
--- a/src/mongo/db/concurrency/lock_state.cpp
+++ b/src/mongo/db/concurrency/lock_state.cpp
@@ -752,6 +752,11 @@ bool LockerImpl::saveLockStateAndUnlock(Locker::LockSnapshot* stateOut) {
return false;
}
+ // If the RSTL is exclusive, then this operation should not yield.
+ if (rstlRequest && rstlRequest->mode != MODE_IX) {
+ return false;
+ }
+
// The global lock must have been acquired just once
stateOut->globalMode = globalRequest->mode;
invariant(unlock(resourceIdGlobal));
diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp
index ec09f87cfe8..49f39041f78 100644
--- a/src/mongo/db/db_raii.cpp
+++ b/src/mongo/db/db_raii.cpp
@@ -53,13 +53,14 @@ const auto allowSecondaryReadsDuringBatchApplication_DONT_USE =
/**
* Performs some checks to determine whether the operation is compatible with a lock-free read.
- * The DBDirectClient and multi-doc transactions are not supported.
+ * Multi-doc transactions are not supported, nor are operations holding an exclusive lock.
*/
bool supportsLockFreeRead(OperationContext* opCtx) {
- // Lock-free reads are only supported for external queries, not internal via DBDirectClient.
// Lock-free reads are not supported in multi-document transactions.
- return !storageGlobalParams.disableLockFreeReads && !opCtx->getClient()->isInDirectClient() &&
- !opCtx->inMultiDocumentTransaction();
+ // Lock-free reads are not supported under an exclusive lock (nested reads under exclusive lock
+ // holding operations).
+ return !storageGlobalParams.disableLockFreeReads && !opCtx->inMultiDocumentTransaction() &&
+ !opCtx->lockState()->isWriteLocked();
}
/**
@@ -78,8 +79,9 @@ bool supportsLockFreeRead(OperationContext* opCtx) {
template <typename GetCollectionAndEstablishReadSourceFunc,
typename GetCollectionAfterSnapshotFunc,
typename ResetFunc>
-auto aquireCollectionAndConsistentSnapshot(
+auto acquireCollectionAndConsistentSnapshot(
OperationContext* opCtx,
+ bool isLockFreeReadSubOperation,
CollectionCatalogStasher& catalogStasher,
GetCollectionAndEstablishReadSourceFunc getCollectionAndEstablishReadSource,
GetCollectionAfterSnapshotFunc getCollectionAfterSnapshot,
@@ -103,6 +105,12 @@ auto aquireCollectionAndConsistentSnapshot(
if (!collection)
break;
+ // If this is a nested lock acquisition, then we already have a consistent stashed catalog
+ // and snapshot from which to read and we can skip the below logic.
+ if (isLockFreeReadSubOperation) {
+ return collection;
+ }
+
// We must open a storage snapshot consistent with the fetched in-memory Collection instance
// and chosen read source. The Collection instance and replication state after opening a
// snapshot will be compared with the previously acquired state. If either does not match,
@@ -363,12 +371,14 @@ AutoGetCollectionForReadLockFree::EmplaceHelper::EmplaceHelper(
CollectionCatalogStasher& catalogStasher,
const NamespaceStringOrUUID& nsOrUUID,
AutoGetCollectionViewMode viewMode,
- Date_t deadline)
+ Date_t deadline,
+ bool isLockFreeReadSubOperation)
: _opCtx(opCtx),
_catalogStasher(catalogStasher),
_nsOrUUID(nsOrUUID),
_viewMode(viewMode),
- _deadline(deadline) {}
+ _deadline(deadline),
+ _isLockFreeReadSubOperation(isLockFreeReadSubOperation) {}
void AutoGetCollectionForReadLockFree::EmplaceHelper::emplace(
boost::optional<AutoGetCollectionLockFree>& autoColl) const {
@@ -376,14 +386,18 @@ void AutoGetCollectionForReadLockFree::EmplaceHelper::emplace(
_opCtx,
_nsOrUUID,
/* restoreFromYield */
- [& catalogStasher = _catalogStasher](std::shared_ptr<const Collection>& collection,
- OperationContext* opCtx,
- CollectionUUID uuid) {
- collection = aquireCollectionAndConsistentSnapshot(
+ [& catalogStasher = _catalogStasher, isSubOperation = _isLockFreeReadSubOperation](
+ std::shared_ptr<const Collection>& collection,
+ OperationContext* opCtx,
+ CollectionUUID uuid) {
+ collection = acquireCollectionAndConsistentSnapshot(
opCtx,
+ /* isLockFreeReadSubOperation */
+ isSubOperation,
+ /* CollectionCatalogStasher */
catalogStasher,
/* GetCollectionAndEstablishReadSourceFunc */
- [uuid](OperationContext* opCtx, const CollectionCatalog& catalog) {
+ [uuid, isSubOperation](OperationContext* opCtx, const CollectionCatalog& catalog) {
auto coll = catalog.lookupCollectionByUUIDForRead(opCtx, uuid);
// After yielding and reacquiring locks, the preconditions that were used to
@@ -418,15 +432,21 @@ AutoGetCollectionForReadLockFree::AutoGetCollectionForReadLockFree(
AutoGetCollectionViewMode viewMode,
Date_t deadline)
: _catalogStash(opCtx) {
+ bool isLockFreeReadSubOperation = opCtx->isLockFreeReadsOp();
+
// Supported lock-free reads should only ever have an open storage snapshot prior to calling
// this helper if it is a nested lock-free operation. The storage snapshot and in-memory state
// used across lock=free reads must be consistent.
invariant(supportsLockFreeRead(opCtx) &&
- (!opCtx->recoveryUnit()->isActive() || opCtx->isLockFreeReadsOp()));
+ (!opCtx->recoveryUnit()->isActive() || isLockFreeReadSubOperation));
- EmplaceHelper emplaceFunc(opCtx, _catalogStash, nsOrUUID, viewMode, deadline);
- aquireCollectionAndConsistentSnapshot(
+ EmplaceHelper emplaceFunc(
+ opCtx, _catalogStash, nsOrUUID, viewMode, deadline, isLockFreeReadSubOperation);
+ acquireCollectionAndConsistentSnapshot(
opCtx,
+ /* isLockFreeReadSubOperation */
+ isLockFreeReadSubOperation,
+ /* CollectionCatalogStasher */
_catalogStash,
/* GetCollectionAndEstablishReadSourceFunc */
[this, &emplaceFunc](OperationContext* opCtx, const CollectionCatalog&) {
@@ -495,7 +515,8 @@ AutoGetCollectionForReadCommandBase<AutoGetCollectionForReadType>::
deadline) {
if (!_autoCollForRead.getView()) {
- auto* const css = CollectionShardingState::get(opCtx, _autoCollForRead.getNss());
+ auto css =
+ CollectionShardingState::getSharedForLockFreeReads(opCtx, _autoCollForRead.getNss());
css->checkShardVersionOrThrow(opCtx);
}
}
diff --git a/src/mongo/db/db_raii.h b/src/mongo/db/db_raii.h
index 6288e2e4e1a..aaa96808282 100644
--- a/src/mongo/db/db_raii.h
+++ b/src/mongo/db/db_raii.h
@@ -227,7 +227,8 @@ private:
CollectionCatalogStasher& catalogStasher,
const NamespaceStringOrUUID& nsOrUUID,
AutoGetCollectionViewMode viewMode,
- Date_t deadline);
+ Date_t deadline,
+ bool isLockFreeReadSubOperation);
void emplace(boost::optional<AutoGetCollectionLockFree>& autoColl) const;
@@ -237,6 +238,10 @@ private:
const NamespaceStringOrUUID& _nsOrUUID;
AutoGetCollectionViewMode _viewMode;
Date_t _deadline;
+
+ // Set to true if the lock helper using this EmplaceHelper is nested under another lock-free
+ // helper.
+ bool _isLockFreeReadSubOperation;
};
boost::optional<AutoGetCollectionForReadBase<AutoGetCollectionLockFree, EmplaceHelper>>
diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp
index 34014831f0b..a4ef8e1c521 100644
--- a/src/mongo/db/pipeline/pipeline_d.cpp
+++ b/src/mongo/db/pipeline/pipeline_d.cpp
@@ -334,9 +334,6 @@ PipelineD::buildInnerQueryExecutor(const CollectionPtr& collection,
return {};
}
- // We are going to generate an input cursor, so we need to be holding the collection lock.
- dassert(expCtx->opCtx->lockState()->isCollectionLockedForMode(nss, MODE_IS));
-
if (!sources.empty()) {
auto sampleStage = dynamic_cast<DocumentSourceSample*>(sources.front().get());
// Optimize an initial $sample stage if possible.
diff --git a/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp b/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp
index 102d9bd74ee..aa253b6cc17 100644
--- a/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp
+++ b/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp
@@ -289,7 +289,7 @@ CommonMongodProcessInterface::attachCursorSourceToPipelineForLocalRead(Pipeline*
invariant(pipeline->getSources().empty() ||
!dynamic_cast<DocumentSourceCursor*>(pipeline->getSources().front().get()));
- boost::optional<AutoGetCollectionForReadCommand> autoColl;
+ boost::optional<AutoGetCollectionForReadCommandMaybeLockFree> autoColl;
const NamespaceStringOrUUID nsOrUUID = expCtx->uuid
? NamespaceStringOrUUID{expCtx->ns.db().toString(), *expCtx->uuid}
: expCtx->ns;
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
index 5cf5b13b9c3..eb57900eb1c 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
@@ -2441,8 +2441,6 @@ void StandardWiredTigerRecordStore::setKey(WT_CURSOR* cursor, RecordId id) const
std::unique_ptr<SeekableRecordCursor> StandardWiredTigerRecordStore::getCursor(
OperationContext* opCtx, bool forward) const {
- dassert(opCtx->lockState()->isReadLocked());
-
if (_isOplog && forward) {
WiredTigerRecoveryUnit* wru = WiredTigerRecoveryUnit::get(opCtx);
// If we already have a snapshot we don't know what it can see, unless we know no one
@@ -2493,8 +2491,6 @@ PrefixedWiredTigerRecordStore::PrefixedWiredTigerRecordStore(WiredTigerKVEngine*
std::unique_ptr<SeekableRecordCursor> PrefixedWiredTigerRecordStore::getCursor(
OperationContext* opCtx, bool forward) const {
- dassert(opCtx->lockState()->isReadLocked());
-
if (_isOplog && forward) {
WiredTigerRecoveryUnit* wru = WiredTigerRecoveryUnit::get(opCtx);
// If we already have a snapshot we don't know what it can see, unless we know no one
diff --git a/src/mongo/dbtests/query_stage_near.cpp b/src/mongo/dbtests/query_stage_near.cpp
index 598b6f9ca2d..f5befb24e1d 100644
--- a/src/mongo/dbtests/query_stage_near.cpp
+++ b/src/mongo/dbtests/query_stage_near.cpp
@@ -233,7 +233,7 @@ TEST_F(QueryStageNearTest, EmptyResults) {
vector<BSONObj> mockData;
WorkingSet workingSet;
- AutoGetCollectionForRead autoColl(_opCtx, NamespaceString{kTestNamespace});
+ AutoGetCollectionForReadMaybeLockFree autoColl(_opCtx, NamespaceString{kTestNamespace});
const auto& coll = autoColl.getCollection();
ASSERT(coll);