summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDianna Hohensee <dianna.hohensee@mongodb.com>2020-10-13 17:52:55 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-14 18:23:38 +0000
commit5a9dd0cbec1f4293e966321885cccb549c77e59c (patch)
tree5277c50be4f1035a813907ebb98598fabf6e6fdc
parent2667abf5f988c9cf9b548e23db961680a24c7a1b (diff)
downloadmongo-5a9dd0cbec1f4293e966321885cccb549c77e59c.tar.gz
SERVER-50677 Make a lock-free read version of AutoGetCollectionForRead and open a storage snapshot corresponding to the acquired Collection instance
-rw-r--r--src/mongo/db/SConscript2
-rw-r--r--src/mongo/db/catalog_raii.cpp3
-rw-r--r--src/mongo/db/catalog_raii.h34
-rw-r--r--src/mongo/db/db_raii.cpp139
-rw-r--r--src/mongo/db/db_raii.h161
-rw-r--r--src/mongo/db/storage/SConscript1
-rw-r--r--src/mongo/embedded/replication_coordinator_embedded.cpp2
7 files changed, 319 insertions, 23 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 5f86b4cbd21..826cafe408a 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -1254,7 +1254,6 @@ env.Library(
'stats/serveronly_stats',
'storage/oplog_hack',
'storage/remove_saver',
- 'storage/snapshot_helper',
'storage/storage_options',
'update/update_driver',
],
@@ -1263,6 +1262,7 @@ env.Library(
'commands/server_status_core',
'kill_sessions',
'stats/resource_consumption_metrics',
+ 'storage/snapshot_helper',
],
)
diff --git a/src/mongo/db/catalog_raii.cpp b/src/mongo/db/catalog_raii.cpp
index f3b9e5aa039..e8bb4e48e74 100644
--- a/src/mongo/db/catalog_raii.cpp
+++ b/src/mongo/db/catalog_raii.cpp
@@ -95,8 +95,7 @@ AutoGetCollection::AutoGetCollection(OperationContext* opCtx,
Database* const db = _autoDb.getDb();
invariant(!nsOrUUID.uuid() || db,
str::stream() << "Database for " << _resolvedNss.ns()
- << " disappeared after successufully resolving "
- << nsOrUUID.toString());
+ << " disappeared after successfully resolving " << nsOrUUID.toString());
// In most cases we expect modifications for system.views to upgrade MODE_IX to MODE_X before
// taking the lock. One exception is a query by UUID of system.views in a transaction. Usual
diff --git a/src/mongo/db/catalog_raii.h b/src/mongo/db/catalog_raii.h
index d5c499c4bc7..4b0ddc7bf0c 100644
--- a/src/mongo/db/catalog_raii.h
+++ b/src/mongo/db/catalog_raii.h
@@ -123,11 +123,8 @@ public:
return getCollection().get();
}
- /**
- * Dereference operator, returns a lvalue reference to the collection.
- */
- const Collection& operator*() const {
- return *getCollection().get();
+ const CollectionPtr& operator*() const {
+ return getCollection();
}
/**
@@ -146,6 +143,8 @@ public:
/**
* Returns nullptr if the collection didn't exist.
+ *
+ * Deprecated in favor of the new ->(), *() and bool() accessors above!
*/
const CollectionPtr& getCollection() const {
return _coll;
@@ -218,14 +217,39 @@ public:
Date_t deadline = Date_t::max());
/**
+ * 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.
+ */
+ AutoGetCollectionLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ LockMode unused, // unused
+ AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max())
+ : AutoGetCollectionLockFree(opCtx, nsOrUUID, viewMode, deadline) {}
+
+ explicit operator bool() const {
+ // Use the CollectionPtr because it is updated if it yields whereas _collection is not until
+ // restore.
+ return static_cast<bool>(_collectionPtr);
+ }
+
+ /**
* AutoGetCollectionLockFree can be used as a Collection pointer with the -> operator.
*/
const Collection* operator->() const {
return getCollection().get();
}
+ const CollectionPtr& operator*() const {
+ return getCollection();
+ }
+
/**
* Returns nullptr if the collection didn't exist.
+ *
+ * Deprecated in favor of the new ->(), *() and bool() accessors above!
*/
const CollectionPtr& getCollection() const {
return _collectionPtr;
diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp
index 7ffe40349ab..600a5b861c4 100644
--- a/src/mongo/db/db_raii.cpp
+++ b/src/mongo/db/db_raii.cpp
@@ -51,6 +51,16 @@ const boost::optional<int> kDoNotChangeProfilingLevel = boost::none;
const auto allowSecondaryReadsDuringBatchApplication_DONT_USE =
OperationContext::declareDecoration<boost::optional<bool>>();
+/**
+ * Performs some checks to determine whether the operation is compatible with a lock-free read.
+ * The DBDirectClient and multi-doc transactions are not supported.
+ */
+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 !opCtx->getClient()->isInDirectClient() && !opCtx->inMultiDocumentTransaction();
+}
+
} // namespace
AutoStatsTracker::AutoStatsTracker(OperationContext* opCtx,
@@ -98,7 +108,10 @@ AutoGetCollectionForReadBase<AutoGetCollectionType>::AutoGetCollectionForReadBas
opCtx->getServiceContext()->getStorageEngine()->supportsReadConcernSnapshot()) {
_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);
repl::ReplicationCoordinator* const replCoord = repl::ReplicationCoordinator::get(opCtx);
@@ -243,6 +256,93 @@ AutoGetCollectionForReadBase<AutoGetCollectionType>::AutoGetCollectionForReadBas
}
}
+AutoGetCollectionForReadLockFree::AutoGetCollectionForReadLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline) {
+ // Supported lock-free reads should never have an open storage snapshot prior to calling
+ // this helper. The storage snapshot and in-memory state fetched here must be consistent.
+ invariant(supportsLockFreeRead(opCtx) && !opCtx->recoveryUnit()->inActiveTxn());
+
+ 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();
+
+ _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 based on replication state. 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.
+
+ opCtx->recoveryUnit()->preallocateSnapshot();
+
+ auto newCollection = CollectionCatalog::get(opCtx).lookupCollectionByUUIDForRead(
+ opCtx, _autoGetCollectionForReadBase.get()->uuid());
+
+ if (_autoGetCollectionForReadBase.get()->getMinimumVisibleSnapshot() ==
+ newCollection->getMinimumVisibleSnapshot() &&
+ replTerm == repl::ReplicationCoordinator::get(opCtx)->getTerm()) {
+ break;
+ }
+
+ LOGV2_DEBUG(5067701,
+ 3,
+ "Retrying acquiring state for lock-free read because collection or replication "
+ "state changed.");
+ _autoGetCollectionForReadBase.reset();
+ opCtx->recoveryUnit()->abandonSnapshot();
+ }
+}
+
+AutoGetCollectionForReadMaybeLockFree::AutoGetCollectionForReadMaybeLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline) {
+ if (supportsLockFreeRead(opCtx)) {
+ _autoGetLockFree.emplace(opCtx, nsOrUUID, viewMode, deadline);
+ } else {
+ _autoGet.emplace(opCtx, nsOrUUID, viewMode, deadline);
+ }
+}
+
+ViewDefinition* AutoGetCollectionForReadMaybeLockFree::getView() const {
+ if (_autoGet) {
+ return _autoGet->getView();
+ } else {
+ return _autoGetLockFree->getView();
+ }
+}
+
+const NamespaceString& AutoGetCollectionForReadMaybeLockFree::getNss() const {
+ if (_autoGet) {
+ return _autoGet->getNss();
+ } else {
+ return _autoGetLockFree->getNss();
+ }
+}
+
+const CollectionPtr& AutoGetCollectionForReadMaybeLockFree::getCollection() const {
+ if (_autoGet) {
+ return _autoGet->getCollection();
+ } else {
+ return _autoGetLockFree->getCollection();
+ }
+}
+
template <typename AutoGetCollectionForReadType>
AutoGetCollectionForReadCommandBase<AutoGetCollectionForReadType>::
AutoGetCollectionForReadCommandBase(OperationContext* opCtx,
@@ -293,6 +393,43 @@ OldClientContext::OldClientContext(OperationContext* opCtx, const std::string& n
CollectionCatalog::get(opCtx).getDatabaseProfileLevel(_db->name()));
}
+AutoGetCollectionForReadCommandMaybeLockFree::AutoGetCollectionForReadCommandMaybeLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline,
+ AutoStatsTracker::LogMode logMode) {
+ if (supportsLockFreeRead(opCtx)) {
+ _autoGetLockFree.emplace(opCtx, nsOrUUID, viewMode, deadline, logMode);
+ } else {
+ _autoGet.emplace(opCtx, nsOrUUID, viewMode, deadline, logMode);
+ }
+}
+
+const CollectionPtr& AutoGetCollectionForReadCommandMaybeLockFree::getCollection() const {
+ if (_autoGet) {
+ return _autoGet->getCollection();
+ } else {
+ return _autoGetLockFree->getCollection();
+ }
+}
+
+ViewDefinition* AutoGetCollectionForReadCommandMaybeLockFree::getView() const {
+ if (_autoGet) {
+ return _autoGet->getView();
+ } else {
+ return _autoGetLockFree->getView();
+ }
+}
+
+const NamespaceString& AutoGetCollectionForReadCommandMaybeLockFree::getNss() const {
+ if (_autoGet) {
+ return _autoGet->getNss();
+ } else {
+ return _autoGetLockFree->getNss();
+ }
+}
+
OldClientContext::~OldClientContext() {
// If in an interrupt, don't record any stats.
// It is possible to have no lock after saving the lock state and being interrupted while
@@ -342,5 +479,7 @@ BlockSecondaryReadsDuringBatchApplication_DONT_USE::
template class AutoGetCollectionForReadBase<AutoGetCollection>;
template class AutoGetCollectionForReadCommandBase<AutoGetCollectionForRead>;
+template class AutoGetCollectionForReadBase<AutoGetCollectionLockFree>;
+template class AutoGetCollectionForReadCommandBase<AutoGetCollectionForReadLockFree>;
} // namespace mongo
diff --git a/src/mongo/db/db_raii.h b/src/mongo/db/db_raii.h
index dbe6b1c1b70..b62a010832f 100644
--- a/src/mongo/db/db_raii.h
+++ b/src/mongo/db/db_raii.h
@@ -104,8 +104,8 @@ public:
return getCollection().get();
}
- Database* getDb() const {
- return _autoColl->getDb();
+ const CollectionPtr& operator*() const {
+ return getCollection();
}
const CollectionPtr& getCollection() const {
@@ -120,7 +120,7 @@ public:
return _autoColl->getNss();
}
-private:
+protected:
// If this field is set, the reader will not take the ParallelBatchWriterMode lock and conflict
// with secondary batch application. This stays in scope with the _autoColl so that locks are
// taken and released in the right order.
@@ -142,7 +142,7 @@ private:
* status of CurrentOp, or add a Top entry.
*
* NOTE: Must not be used with any locks held, because it needs to block waiting on the committed
- * snapshot to become available.
+ * snapshot to become available, and can potentially release and reacquire locks.
*/
class AutoGetCollectionForRead : public AutoGetCollectionForReadBase<AutoGetCollection> {
public:
@@ -152,6 +152,85 @@ public:
AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
Date_t deadline = Date_t::max())
: AutoGetCollectionForReadBase(opCtx, nsOrUUID, viewMode, deadline) {}
+
+ Database* getDb() const {
+ return _autoColl->getDb();
+ }
+};
+
+/**
+ * Same as AutoGetCollectionForRead above except does not take collection, database or rstl locks.
+ * Takes the global lock and may take the PBWM, same as AutoGetCollectionForRead. Ensures a
+ * consistent in-memory and on-disk view of the collection.
+ */
+class AutoGetCollectionForReadLockFree {
+public:
+ AutoGetCollectionForReadLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max());
+
+ explicit operator bool() const {
+ return static_cast<bool>(getCollection());
+ }
+
+ const Collection* operator->() const {
+ return getCollection().get();
+ }
+
+ const CollectionPtr& operator*() const {
+ return getCollection();
+ }
+
+ const CollectionPtr& getCollection() const {
+ return _autoGetCollectionForReadBase->getCollection();
+ }
+
+ ViewDefinition* getView() const {
+ return _autoGetCollectionForReadBase->getView();
+ }
+
+ const NamespaceString& getNss() const {
+ return _autoGetCollectionForReadBase->getNss();
+ }
+
+private:
+ boost::optional<AutoGetCollectionForReadBase<AutoGetCollectionLockFree>>
+ _autoGetCollectionForReadBase;
+};
+
+/**
+ * Creates either an AutoGetCollectionForRead or AutoGetCollectionForReadLockFree depending on
+ * whether a lock-free read is supported.
+ */
+class AutoGetCollectionForReadMaybeLockFree {
+public:
+ AutoGetCollectionForReadMaybeLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max());
+
+ /**
+ * Passthrough functions to either _autoGet or _autoGetLockFree.
+ */
+ explicit operator bool() const {
+ return static_cast<bool>(getCollection());
+ }
+ const Collection* operator->() const {
+ return getCollection().get();
+ }
+ const CollectionPtr& operator*() const {
+ return getCollection();
+ }
+ const CollectionPtr& getCollection() const;
+ ViewDefinition* getView() const;
+ const NamespaceString& getNss() const;
+
+private:
+ boost::optional<AutoGetCollectionForRead> _autoGet;
+ boost::optional<AutoGetCollectionForReadLockFree> _autoGetLockFree;
};
template <typename AutoGetCollectionForReadType>
@@ -161,13 +240,6 @@ class AutoGetCollectionForReadCommandBase {
delete;
public:
- AutoGetCollectionForReadCommandBase(
- OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
- AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
- Date_t deadline = Date_t::max(),
- AutoStatsTracker::LogMode logMode = AutoStatsTracker::LogMode::kUpdateTopAndCurOp);
-
explicit operator bool() const {
return static_cast<bool>(getCollection());
}
@@ -176,8 +248,8 @@ public:
return getCollection().get();
}
- Database* getDb() const {
- return _autoCollForRead.getDb();
+ const CollectionPtr& operator*() const {
+ return getCollection();
}
const CollectionPtr& getCollection() const {
@@ -192,7 +264,14 @@ public:
return _autoCollForRead.getNss();
}
-private:
+protected:
+ AutoGetCollectionForReadCommandBase(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max(),
+ AutoStatsTracker::LogMode logMode = AutoStatsTracker::LogMode::kUpdateTopAndCurOp);
+
AutoGetCollectionForReadType _autoCollForRead;
AutoStatsTracker _statsTracker;
};
@@ -211,6 +290,60 @@ public:
Date_t deadline = Date_t::max(),
AutoStatsTracker::LogMode logMode = AutoStatsTracker::LogMode::kUpdateTopAndCurOp)
: AutoGetCollectionForReadCommandBase(opCtx, nsOrUUID, viewMode, deadline, logMode) {}
+
+ Database* getDb() const {
+ return _autoCollForRead.getDb();
+ }
+};
+
+/**
+ * Same as AutoGetCollectionForReadCommand except no collection, database or RSTL lock is taken.
+ */
+class AutoGetCollectionForReadCommandLockFree
+ : public AutoGetCollectionForReadCommandBase<AutoGetCollectionForReadLockFree> {
+public:
+ AutoGetCollectionForReadCommandLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max(),
+ AutoStatsTracker::LogMode logMode = AutoStatsTracker::LogMode::kUpdateTopAndCurOp)
+ : AutoGetCollectionForReadCommandBase(opCtx, nsOrUUID, viewMode, deadline, logMode) {}
+};
+
+/**
+ * Creates either an AutoGetCollectionForReadCommand or AutoGetCollectionForReadCommandLockFree
+ * depending on whether a lock-free read is supported in the situation per the results of
+ * supportsLockFreeRead().
+ */
+class AutoGetCollectionForReadCommandMaybeLockFree {
+public:
+ AutoGetCollectionForReadCommandMaybeLockFree(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode = AutoGetCollectionViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max(),
+ AutoStatsTracker::LogMode logMode = AutoStatsTracker::LogMode::kUpdateTopAndCurOp);
+
+ /**
+ * Passthrough function to either _autoGet or _autoGetLockFree.
+ */
+ explicit operator bool() const {
+ return static_cast<bool>(getCollection());
+ }
+ const Collection* operator->() const {
+ return getCollection().get();
+ }
+ const CollectionPtr& operator*() const {
+ return getCollection();
+ }
+ const CollectionPtr& getCollection() const;
+ ViewDefinition* getView() const;
+ const NamespaceString& getNss() const;
+
+private:
+ boost::optional<AutoGetCollectionForReadCommand> _autoGet;
+ boost::optional<AutoGetCollectionForReadCommandLockFree> _autoGetLockFree;
};
/**
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index d8fc600d89b..46dfc0ecc9b 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -38,6 +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/repl/read_concern_args',
'$BUILD_DIR/mongo/db/repl/repl_coordinator_interface',
diff --git a/src/mongo/embedded/replication_coordinator_embedded.cpp b/src/mongo/embedded/replication_coordinator_embedded.cpp
index e8e43f17bdb..72b956887ca 100644
--- a/src/mongo/embedded/replication_coordinator_embedded.cpp
+++ b/src/mongo/embedded/replication_coordinator_embedded.cpp
@@ -459,7 +459,7 @@ Status ReplicationCoordinatorEmbedded::processHeartbeatV1(const ReplSetHeartbeat
}
long long ReplicationCoordinatorEmbedded::getTerm() const {
- UASSERT_NOT_IMPLEMENTED;
+ return 3; // arbitrary constant number
}
Status ReplicationCoordinatorEmbedded::updateTerm(OperationContext*, long long) {