diff options
-rw-r--r-- | src/mongo/db/catalog_raii.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/count_cmd.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/commands/distinct.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/map_reduce_agg.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/db_raii.cpp | 53 | ||||
-rw-r--r-- | src/mongo/db/db_raii.h | 7 | ||||
-rw-r--r-- | src/mongo/db/pipeline/pipeline_d.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp | 4 | ||||
-rw-r--r-- | src/mongo/dbtests/query_stage_near.cpp | 2 |
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); |