summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorHenrik Edin <henrik.edin@mongodb.com>2020-10-23 10:04:36 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-17 22:51:50 +0000
commit289b22035a4f7ebad120fedda30458bb3b74665b (patch)
treeab25572e53c44ccefbe1aeef2144739374e079cb /src/mongo
parent92cc84b0171942375ccbd2312a052bc7e9f159dd (diff)
downloadmongo-289b22035a4f7ebad120fedda30458bb3b74665b.tar.gz
SERVER-50870 Establish consistent storage snapshot with the refreshed Collection after yielding
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/catalog/SConscript1
-rw-r--r--src/mongo/db/catalog/collection_catalog.cpp17
-rw-r--r--src/mongo/db/catalog_raii.cpp13
-rw-r--r--src/mongo/db/catalog_raii.h22
-rw-r--r--src/mongo/db/concurrency/SConscript11
-rw-r--r--src/mongo/db/concurrency/lock_manager_defs.cpp42
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp8
-rw-r--r--src/mongo/db/db_raii.cpp249
-rw-r--r--src/mongo/db/db_raii.h63
-rw-r--r--src/mongo/db/query/plan_yield_policy_impl.cpp26
-rw-r--r--src/mongo/db/storage/SConscript3
12 files changed, 336 insertions, 120 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 3ae923af8f6..a12cb0f9b76 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -1257,7 +1257,6 @@ env.Library(
'commands/server_status_core',
'kill_sessions',
'stats/resource_consumption_metrics',
- 'storage/snapshot_helper',
],
)
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 84c782e70a8..2e7152c8a7d 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -272,6 +272,7 @@ env.Library(
'$BUILD_DIR/mongo/db/namespace_string',
'$BUILD_DIR/mongo/db/profile_filter',
'$BUILD_DIR/mongo/db/service_context',
+ '$BUILD_DIR/mongo/db/storage/snapshot_helper',
'collection',
],
LIBDEPS_PRIVATE=[
diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp
index e6e28b0d920..671417978c8 100644
--- a/src/mongo/db/catalog/collection_catalog.cpp
+++ b/src/mongo/db/catalog/collection_catalog.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/server_options.h"
#include "mongo/db/storage/recovery_unit.h"
+#include "mongo/db/storage/snapshot_helper.h"
#include "mongo/logv2/log.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/uuid.h"
@@ -1037,7 +1038,21 @@ void CollectionCatalogStasher::reset() {
const Collection* LookupCollectionForYieldRestore::operator()(OperationContext* opCtx,
CollectionUUID uuid) const {
- return CollectionCatalog::get(opCtx)->lookupCollectionByUUID(opCtx, uuid).get();
+ auto collection = CollectionCatalog::get(opCtx)->lookupCollectionByUUID(opCtx, uuid).get();
+ if (!collection)
+ return nullptr;
+
+ // After yielding and reacquiring locks, the preconditions that were used to select our
+ // ReadSource initially need to be checked again. We select a ReadSource based on replication
+ // state. After a query yields its locks, the replication state may have changed, invalidating
+ // our current choice of ReadSource. Using the same preconditions, change our ReadSource if
+ // necessary.
+ auto newReadSource = SnapshotHelper::getNewReadSource(opCtx, collection->ns());
+ if (newReadSource) {
+ opCtx->recoveryUnit()->setTimestampReadSource(*newReadSource);
+ }
+
+ return collection;
}
} // namespace mongo
diff --git a/src/mongo/db/catalog_raii.cpp b/src/mongo/db/catalog_raii.cpp
index ba7dbe54cc2..92e0b9c4f96 100644
--- a/src/mongo/db/catalog_raii.cpp
+++ b/src/mongo/db/catalog_raii.cpp
@@ -207,6 +207,7 @@ Collection* AutoGetCollection::getWritableCollection(CollectionCatalog::Lifetime
AutoGetCollectionLockFree::AutoGetCollectionLockFree(OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
+ RestoreFromYieldFn restoreFromYield,
AutoGetCollectionViewMode viewMode,
Date_t deadline)
: _lockFreeReadsBlock(opCtx),
@@ -223,11 +224,13 @@ AutoGetCollectionLockFree::AutoGetCollectionLockFree(OperationContext* opCtx,
// When we restore from yield on this CollectionPtr we will update _collection above and use its
// new pointer in the CollectionPtr
- _collectionPtr = CollectionPtr(
- opCtx, _collection.get(), [this](OperationContext* opCtx, CollectionUUID uuid) {
- _collection = CollectionCatalog::get(opCtx)->lookupCollectionByUUIDForRead(opCtx, uuid);
- return _collection.get();
- });
+ _collectionPtr = CollectionPtr(opCtx,
+ _collection.get(),
+ [this, restoreFromYield = std::move(restoreFromYield)](
+ OperationContext* opCtx, CollectionUUID uuid) {
+ restoreFromYield(_collection, opCtx, uuid);
+ return _collection.get();
+ });
// TODO (SERVER-51319): add DatabaseShardingState::checkDbVersion somewhere.
diff --git a/src/mongo/db/catalog_raii.h b/src/mongo/db/catalog_raii.h
index f5e7a8e5f26..6ddde54c1c3 100644
--- a/src/mongo/db/catalog_raii.h
+++ b/src/mongo/db/catalog_raii.h
@@ -206,30 +206,30 @@ protected:
* The collection references returned by this class will no longer be safe to retain after this
* object goes out of scope. This object ensures the continued existence of a Collection reference,
* if the collection exists when this object is instantiated.
+ *
+ * This class is only used by AutoGetCollectionForReadLockFree.
*/
class AutoGetCollectionLockFree {
AutoGetCollectionLockFree(const AutoGetCollectionLockFree&) = delete;
AutoGetCollectionLockFree& operator=(const AutoGetCollectionLockFree&) = delete;
public:
- AutoGetCollectionLockFree(
- OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
- AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
- Date_t deadline = Date_t::max());
+ /**
+ * Function used to customize restore after yield behavior
+ */
+ using RestoreFromYieldFn =
+ std::function<void(std::shared_ptr<const Collection>&, OperationContext*, CollectionUUID)>;
/**
- * Same constructor as above except it accepts a fifth unused LockMode parameter in order to
- * parallel AutoGetCollection and meet the templated AutoGetCollectionForReadBase class'
- * type structure expectations.
+ * Used by AutoGetCollectionForReadLockFree where it provides implementation for restore after
+ * yield.
*/
AutoGetCollectionLockFree(
OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
- LockMode unused, // unused
+ RestoreFromYieldFn restoreFromYield,
AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
- Date_t deadline = Date_t::max())
- : AutoGetCollectionLockFree(opCtx, nsOrUUID, viewMode, deadline) {}
+ Date_t deadline = Date_t::max());
explicit operator bool() const {
// Use the CollectionPtr because it is updated if it yields whereas _collection is not until
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript
index 892e7364cc8..37b0095d141 100644
--- a/src/mongo/db/concurrency/SConscript
+++ b/src/mongo/db/concurrency/SConscript
@@ -37,6 +37,16 @@ env.Library(
)
env.Library(
+ target='lock_manager_defs',
+ source=[
+ 'lock_manager_defs.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ],
+)
+
+env.Library(
target='lock_manager',
source=[
'd_concurrency.cpp',
@@ -52,6 +62,7 @@ env.Library(
'$BUILD_DIR/mongo/util/concurrency/spin_lock',
'$BUILD_DIR/mongo/util/concurrency/ticketholder',
'$BUILD_DIR/third_party/shim_boost',
+ 'lock_manager_defs',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/catalog/collection_catalog',
diff --git a/src/mongo/db/concurrency/lock_manager_defs.cpp b/src/mongo/db/concurrency/lock_manager_defs.cpp
new file mode 100644
index 00000000000..01ae4586cad
--- /dev/null
+++ b/src/mongo/db/concurrency/lock_manager_defs.cpp
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2020-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.
+ */
+
+#include "lock_manager_defs.h"
+
+namespace mongo {
+
+// Hardcoded resource IDs.
+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);
+
+} // namespace mongo
diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp
index 416c8c5e7e2..3be2cebb251 100644
--- a/src/mongo/db/concurrency/lock_state.cpp
+++ b/src/mongo/db/concurrency/lock_state.cpp
@@ -1109,12 +1109,4 @@ void resetGlobalLockStats() {
globalStats.reset();
}
-// Hardcoded resource IDs.
-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);
-
} // namespace mongo
diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp
index cb1d875cb1f..2b146c8ce26 100644
--- a/src/mongo/db/db_raii.cpp
+++ b/src/mongo/db/db_raii.cpp
@@ -62,6 +62,90 @@ bool supportsLockFreeRead(OperationContext* opCtx) {
!opCtx->inMultiDocumentTransaction();
}
+/**
+ * Helper function to acquire a collection and consistent snapshot without holding the RSTL or
+ * collection locks.
+ *
+ * GetCollectionAndEstablishReadSourceFunc is called before we open a snapshot, it needs to fetch
+ * the Collection from the catalog and select the read source.
+ *
+ * GetCollectionAfterSnapshotFunc is called after the snapshot is opened, it needs to fetch the
+ * Collection from the catalog that is used to compare consistency with the Collection returned by
+ * GetCollectionAndEstablishReadSourceFunc.
+ *
+ * ResetFunc is called when we failed to achieve consistency and need to retry.
+ */
+template <typename GetCollectionAndEstablishReadSourceFunc,
+ typename GetCollectionAfterSnapshotFunc,
+ typename ResetFunc>
+auto aquireCollectionAndConsistentSnapshot(
+ OperationContext* opCtx,
+ CollectionCatalogStasher& catalogStasher,
+ GetCollectionAndEstablishReadSourceFunc getCollectionAndEstablishReadSource,
+ GetCollectionAfterSnapshotFunc getCollectionAfterSnapshot,
+ ResetFunc reset) {
+ // Figure out what type of Collection GetCollectionAndEstablishReadSourceFunc returns. It needs
+ // to behave like a pointer.
+ using CollectionPtrT = decltype(std::declval<GetCollectionAndEstablishReadSourceFunc>()(
+ std::declval<OperationContext*>(), std::declval<const CollectionCatalog&>()));
+
+ CollectionPtrT collection;
+ catalogStasher.reset();
+ while (true) {
+ // AutoGetCollectionForReadBase can choose a read source based on the current replication
+ // state. Therefore we must fetch the repl state beforehand, to compare with afterwards.
+ long long replTerm = repl::ReplicationCoordinator::get(opCtx)->getTerm();
+
+ auto catalog = CollectionCatalog::get(opCtx);
+ collection = getCollectionAndEstablishReadSource(opCtx, *catalog);
+
+ // A lock request does not always find a collection to lock.
+ if (!collection)
+ break;
+
+ // 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,
+ // then this loop will retry lock acquisition and read source selection until there is a
+ // match.
+ //
+ // Note: getCollectionAndEstablishReadSource() may open a snapshot for PIT reads, so
+ // preallocateSnapshot() may be a no-op, but that is OK because the snapshot is established
+ // by getCollectionAndEstablishReadSource() after it fetches a Collection instance.
+ if (collection->ns().isOplog()) {
+ // Signal to the RecoveryUnit that the snapshot will be used for reading the oplog.
+ // Normally the snapshot is opened from a cursor that can take special action when
+ // reading from the oplog.
+ opCtx->recoveryUnit()->preallocateSnapshotForOplogRead();
+ } else {
+ opCtx->recoveryUnit()->preallocateSnapshot();
+ }
+
+ // The collection may have been dropped since the previous lookup, run the loop one more
+ // time to cleanup if newCollection is nullptr
+ auto newCatalog = CollectionCatalog::get(opCtx);
+ if (catalog == newCatalog) {
+ auto newCollection = getCollectionAfterSnapshot(opCtx, *catalog);
+ if (newCollection && catalog == newCatalog &&
+ collection->getMinimumVisibleSnapshot() ==
+ newCollection->getMinimumVisibleSnapshot() &&
+ replTerm == repl::ReplicationCoordinator::get(opCtx)->getTerm()) {
+ catalogStasher.stash(std::move(catalog));
+ break;
+ }
+ }
+
+ LOGV2_DEBUG(5067701,
+ 3,
+ "Retrying acquiring state for lock-free read because collection or replication "
+ "state changed.");
+ reset();
+ opCtx->recoveryUnit()->abandonSnapshot();
+ }
+
+ return collection;
+}
+
} // namespace
AutoStatsTracker::AutoStatsTracker(OperationContext* opCtx,
@@ -95,12 +179,10 @@ AutoStatsTracker::~AutoStatsTracker() {
curOp->getReadWriteType());
}
-template <typename AutoGetCollectionType>
-AutoGetCollectionForReadBase<AutoGetCollectionType>::AutoGetCollectionForReadBase(
- OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
- AutoGetCollectionViewMode viewMode,
- Date_t deadline) {
+template <typename AutoGetCollectionType, typename EmplaceAutoCollFunc>
+AutoGetCollectionForReadBase<AutoGetCollectionType, EmplaceAutoCollFunc>::
+ AutoGetCollectionForReadBase(OperationContext* opCtx,
+ const EmplaceAutoCollFunc& emplaceAutoColl) {
// The caller was expecting to conflict with batch application before entering this function.
// i.e. the caller does not currently have a ShouldNotConflict... block in scope.
bool callerWasConflicting = opCtx->lockState()->shouldConflictWithSecondaryBatchApplication();
@@ -110,10 +192,7 @@ AutoGetCollectionForReadBase<AutoGetCollectionType>::AutoGetCollectionForReadBas
_shouldNotConflictWithSecondaryBatchApplicationBlock.emplace(opCtx->lockState());
}
- // Multi-document transactions need MODE_IX locks, otherwise MODE_IS.
- const auto collectionLockMode = getLockModeForQuery(opCtx, nsOrUUID.nss());
-
- _autoColl.emplace(opCtx, nsOrUUID, collectionLockMode, viewMode, deadline);
+ emplaceAutoColl.emplace(_autoColl);
repl::ReplicationCoordinator* const replCoord = repl::ReplicationCoordinator::get(opCtx);
const auto readConcernLevel = repl::ReadConcernArgs::get(opCtx).getLevel();
@@ -253,10 +332,84 @@ AutoGetCollectionForReadBase<AutoGetCollectionType>::AutoGetCollectionForReadBas
CurOp::get(opCtx)->yielded();
}
- _autoColl.emplace(opCtx, nsOrUUID, collectionLockMode, viewMode, deadline);
+ emplaceAutoColl.emplace(_autoColl);
}
}
+EmplaceAutoGetCollectionForRead::EmplaceAutoGetCollectionForRead(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline)
+ : _opCtx(opCtx), _nsOrUUID(nsOrUUID), _viewMode(viewMode), _deadline(deadline) {
+ // Multi-document transactions need MODE_IX locks, otherwise MODE_IS.
+ _collectionLockMode = getLockModeForQuery(opCtx, nsOrUUID.nss());
+}
+
+void EmplaceAutoGetCollectionForRead::emplace(boost::optional<AutoGetCollection>& autoColl) const {
+ autoColl.emplace(_opCtx, _nsOrUUID, _collectionLockMode, _viewMode, _deadline);
+}
+
+AutoGetCollectionForRead::AutoGetCollectionForRead(OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline)
+ : AutoGetCollectionForReadBase(
+ opCtx, EmplaceAutoGetCollectionForRead(opCtx, nsOrUUID, viewMode, deadline)) {}
+
+AutoGetCollectionForReadLockFree::EmplaceHelper::EmplaceHelper(
+ OperationContext* opCtx,
+ CollectionCatalogStasher& catalogStasher,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline)
+ : _opCtx(opCtx),
+ _catalogStasher(catalogStasher),
+ _nsOrUUID(nsOrUUID),
+ _viewMode(viewMode),
+ _deadline(deadline) {}
+
+void AutoGetCollectionForReadLockFree::EmplaceHelper::emplace(
+ boost::optional<AutoGetCollectionLockFree>& autoColl) const {
+ autoColl.emplace(
+ _opCtx,
+ _nsOrUUID,
+ /* restoreFromYield */
+ [& catalogStasher = _catalogStasher](std::shared_ptr<const Collection>& collection,
+ OperationContext* opCtx,
+ CollectionUUID uuid) {
+ collection = aquireCollectionAndConsistentSnapshot(
+ opCtx,
+ catalogStasher,
+ /* GetCollectionAndEstablishReadSourceFunc */
+ [uuid](OperationContext* opCtx, const CollectionCatalog& catalog) {
+ auto coll = catalog.lookupCollectionByUUIDForRead(opCtx, uuid);
+
+ // After yielding and reacquiring locks, the preconditions that were used to
+ // select our ReadSource initially need to be checked again. We select a
+ // ReadSource based on replication state. After a query yields its locks, the
+ // replication state may have changed, invalidating our current choice of
+ // ReadSource. Using the same preconditions, change our ReadSource if necessary.
+ if (coll) {
+ auto newReadSource = SnapshotHelper::getNewReadSource(opCtx, coll->ns());
+ if (newReadSource) {
+ opCtx->recoveryUnit()->setTimestampReadSource(*newReadSource);
+ }
+ }
+
+ return coll;
+ },
+ /* GetCollectionAfterSnapshotFunc */
+ [uuid](OperationContext* opCtx, const CollectionCatalog& catalog) {
+ return catalog.lookupCollectionByUUIDForRead(opCtx, uuid);
+ },
+ /* ResetFunc */
+ []() {});
+ },
+ _viewMode,
+ _deadline);
+}
+
AutoGetCollectionForReadLockFree::AutoGetCollectionForReadLockFree(
OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
@@ -267,59 +420,22 @@ AutoGetCollectionForReadLockFree::AutoGetCollectionForReadLockFree(
// this helper. The storage snapshot and in-memory state fetched here must be consistent.
invariant(supportsLockFreeRead(opCtx) && !opCtx->recoveryUnit()->isActive());
- while (true) {
- // AutoGetCollectionForReadBase can choose a read source based on the current replication
- // state. Therefore we must fetch the repl state beforehand, to compare with afterwards.
- long long replTerm = repl::ReplicationCoordinator::get(opCtx)->getTerm();
-
- auto catalog = CollectionCatalog::get(opCtx);
- _autoGetCollectionForReadBase.emplace(opCtx, nsOrUUID, viewMode, deadline);
-
- // A lock request does not always find a collection to lock.
- if (!_autoGetCollectionForReadBase.get()) {
- break;
- }
-
- // 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,
- // then this loop will retry lock acquisition and read source selection until there is a
- // match.
- //
- // Note: AutoGetCollectionForReadBase may open a snapshot for PIT reads, so
- // preallocateSnapshot() may be a no-op, but that is OK because the snapshot is established
- // by _autoGetCollectionForReadBase after it fetches a Collection instance.
-
- if (_autoGetCollectionForReadBase->getNss().isOplog()) {
- // Signal to the RecoveryUnit that the snapshot will be used for reading the oplog.
- // Normally the snapshot is opened from a cursor that can take special action when
- // reading from the oplog.
- opCtx->recoveryUnit()->preallocateSnapshotForOplogRead();
- } else {
- opCtx->recoveryUnit()->preallocateSnapshot();
- }
-
- auto newCatalog = CollectionCatalog::get(opCtx);
- auto newCollection = newCatalog->lookupCollectionByUUIDForRead(
- opCtx, _autoGetCollectionForReadBase.get()->uuid());
-
- // The collection may have been dropped since the previous lookup, run the loop one more
- // time to cleanup if newCollection is nullptr
- if (newCollection && catalog == newCatalog &&
- _autoGetCollectionForReadBase.get()->getMinimumVisibleSnapshot() ==
- newCollection->getMinimumVisibleSnapshot() &&
- replTerm == repl::ReplicationCoordinator::get(opCtx)->getTerm()) {
- _catalogStash.stash(std::move(catalog));
- break;
- }
-
- LOGV2_DEBUG(5067701,
- 3,
- "Retrying acquiring state for lock-free read because collection or replication "
- "state changed.");
- _autoGetCollectionForReadBase.reset();
- opCtx->recoveryUnit()->abandonSnapshot();
- }
+ EmplaceHelper emplaceFunc(opCtx, _catalogStash, nsOrUUID, viewMode, deadline);
+ aquireCollectionAndConsistentSnapshot(
+ opCtx,
+ _catalogStash,
+ /* GetCollectionAndEstablishReadSourceFunc */
+ [this, &emplaceFunc](OperationContext* opCtx, const CollectionCatalog&) {
+ _autoGetCollectionForReadBase.emplace(opCtx, emplaceFunc);
+ return _autoGetCollectionForReadBase->getCollection().get();
+ },
+ /* GetCollectionAfterSnapshotFunc */
+ [this](OperationContext* opCtx, const CollectionCatalog& catalog) {
+ return catalog.lookupCollectionByUUIDForRead(
+ opCtx, _autoGetCollectionForReadBase.get()->uuid());
+ },
+ /* ResetFunc */
+ [this]() { _autoGetCollectionForReadBase.reset(); });
}
AutoGetCollectionForReadMaybeLockFree::AutoGetCollectionForReadMaybeLockFree(
@@ -492,9 +608,10 @@ BlockSecondaryReadsDuringBatchApplication_DONT_USE::
allowSecondaryReads->swap(_originalSettings);
}
-template class AutoGetCollectionForReadBase<AutoGetCollection>;
+template class AutoGetCollectionForReadBase<AutoGetCollection, EmplaceAutoGetCollectionForRead>;
template class AutoGetCollectionForReadCommandBase<AutoGetCollectionForRead>;
-template class AutoGetCollectionForReadBase<AutoGetCollectionLockFree>;
+template class AutoGetCollectionForReadBase<AutoGetCollectionLockFree,
+ AutoGetCollectionForReadLockFree::EmplaceHelper>;
template class AutoGetCollectionForReadCommandBase<AutoGetCollectionForReadLockFree>;
} // namespace mongo
diff --git a/src/mongo/db/db_raii.h b/src/mongo/db/db_raii.h
index d8d7a0a3390..57c8d8a1753 100644
--- a/src/mongo/db/db_raii.h
+++ b/src/mongo/db/db_raii.h
@@ -84,17 +84,18 @@ private:
const LogMode _logMode;
};
-template <typename AutoGetCollectionType>
+/**
+ * Shared base class for AutoGetCollectionForRead and AutoGetCollectionForReadLockFree.
+ * Do not use directly.
+ */
+template <typename AutoGetCollectionType, typename EmplaceAutoGetCollectionFunc>
class AutoGetCollectionForReadBase {
AutoGetCollectionForReadBase(const AutoGetCollectionForReadBase&) = delete;
AutoGetCollectionForReadBase& operator=(const AutoGetCollectionForReadBase&) = delete;
public:
- AutoGetCollectionForReadBase(
- OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
- AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
- Date_t deadline = Date_t::max());
+ AutoGetCollectionForReadBase(OperationContext* opCtx,
+ const EmplaceAutoGetCollectionFunc& emplaceAutoColl);
explicit operator bool() const {
return static_cast<bool>(getCollection());
@@ -133,6 +134,27 @@ protected:
};
/**
+ * Helper for AutoGetCollectionForRead below. Contains implementation on how contained
+ * AutoGetCollection is instantiated by AutoGetCollectionForReadBase.
+ */
+class EmplaceAutoGetCollectionForRead {
+public:
+ EmplaceAutoGetCollectionForRead(OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline);
+
+ void emplace(boost::optional<AutoGetCollection>& autoColl) const;
+
+private:
+ OperationContext* _opCtx;
+ const NamespaceStringOrUUID& _nsOrUUID;
+ AutoGetCollectionViewMode _viewMode;
+ Date_t _deadline;
+ LockMode _collectionLockMode;
+};
+
+/**
* Same as calling AutoGetCollection with MODE_IS, but in addition ensures that the read will be
* performed against an appropriately committed snapshot if the operation is using a readConcern of
* 'majority'.
@@ -144,14 +166,14 @@ protected:
* NOTE: Must not be used with any locks held, because it needs to block waiting on the committed
* snapshot to become available, and can potentially release and reacquire locks.
*/
-class AutoGetCollectionForRead : public AutoGetCollectionForReadBase<AutoGetCollection> {
+class AutoGetCollectionForRead
+ : public AutoGetCollectionForReadBase<AutoGetCollection, EmplaceAutoGetCollectionForRead> {
public:
AutoGetCollectionForRead(
OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
- Date_t deadline = Date_t::max())
- : AutoGetCollectionForReadBase(opCtx, nsOrUUID, viewMode, deadline) {}
+ Date_t deadline = Date_t::max());
Database* getDb() const {
return _autoColl->getDb();
@@ -196,7 +218,28 @@ public:
}
private:
- boost::optional<AutoGetCollectionForReadBase<AutoGetCollectionLockFree>>
+ /**
+ * Helper for how AutoGetCollectionForReadBase instantiates its owned AutoGetCollectionLockFree.
+ */
+ class EmplaceHelper {
+ public:
+ EmplaceHelper(OperationContext* opCtx,
+ CollectionCatalogStasher& catalogStasher,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline);
+
+ void emplace(boost::optional<AutoGetCollectionLockFree>& autoColl) const;
+
+ private:
+ OperationContext* _opCtx;
+ CollectionCatalogStasher& _catalogStasher;
+ const NamespaceStringOrUUID& _nsOrUUID;
+ AutoGetCollectionViewMode _viewMode;
+ Date_t _deadline;
+ };
+
+ boost::optional<AutoGetCollectionForReadBase<AutoGetCollectionLockFree, EmplaceHelper>>
_autoGetCollectionForReadBase;
CollectionCatalogStasher _catalogStash;
};
diff --git a/src/mongo/db/query/plan_yield_policy_impl.cpp b/src/mongo/db/query/plan_yield_policy_impl.cpp
index e5492c2939d..c1fdd445472 100644
--- a/src/mongo/db/query/plan_yield_policy_impl.cpp
+++ b/src/mongo/db/query/plan_yield_policy_impl.cpp
@@ -36,7 +36,6 @@
#include "mongo/db/curop_failpoint_helpers.h"
#include "mongo/db/query/query_knobs_gen.h"
#include "mongo/db/service_context.h"
-#include "mongo/db/storage/snapshot_helper.h"
#include "mongo/util/fail_point.h"
namespace mongo {
@@ -119,13 +118,12 @@ void PlanYieldPolicyImpl::_yieldAllLocks(OperationContext* opCtx,
opCtx->checkForInterrupt(); // throws
}
- ON_BLOCK_EXIT([yieldable]() {
- if (yieldable)
- yieldable->restore();
- });
-
if (!unlocked) {
- // Nothing was unlocked, just return, yielding is pointless.
+ // Nothing was unlocked, just return, yielding is pointless. Restore the yieldable before
+ // returning.
+ if (yieldable) {
+ yieldable->restore();
+ }
return;
}
@@ -144,15 +142,11 @@ void PlanYieldPolicyImpl::_yieldAllLocks(OperationContext* opCtx,
locker->restoreLockState(opCtx, snapshot);
- // After yielding and reacquiring locks, the preconditions that were used to select our
- // ReadSource initially need to be checked again. Queries hold an AutoGetCollectionForRead RAII
- // lock for their lifetime, which may select a ReadSource based on state (e.g. replication
- // state). After a query yields its locks, this state may have changed, invalidating our current
- // choice of ReadSource. Using the same preconditions, change our ReadSource if necessary.
- auto newReadSource = SnapshotHelper::getNewReadSource(opCtx, planExecNS);
- if (newReadSource) {
- opCtx->recoveryUnit()->setTimestampReadSource(*newReadSource);
- }
+ // If we get this far we should have a yieldable instance.
+ invariant(yieldable);
+
+ // Yieldable restore may set a new read source if necessary
+ yieldable->restore();
}
void PlanYieldPolicyImpl::preCheckInterruptOnly(OperationContext* opCtx) {
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index 2b3a2703cb4..e138a247664 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -38,8 +38,7 @@ env.Library(
'$BUILD_DIR/mongo/db/namespace_string',
],
LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/db/catalog/collection_catalog',
- '$BUILD_DIR/mongo/db/concurrency/lock_manager',
+ '$BUILD_DIR/mongo/db/concurrency/lock_manager_defs',
'$BUILD_DIR/mongo/db/repl/read_concern_args',
'$BUILD_DIR/mongo/db/repl/repl_coordinator_interface',
'recovery_unit_base',