summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/catalog/collection.h6
-rw-r--r--src/mongo/db/catalog/database_holder.h13
-rw-r--r--src/mongo/db/catalog/database_holder_impl.cpp13
-rw-r--r--src/mongo/db/catalog/database_holder_impl.h3
-rw-r--r--src/mongo/db/catalog/database_holder_mock.h5
-rw-r--r--src/mongo/db/catalog_raii.cpp37
-rw-r--r--src/mongo/db/catalog_raii.h65
-rw-r--r--src/mongo/db/concurrency/d_concurrency.cpp32
-rw-r--r--src/mongo/db/concurrency/d_concurrency.h12
-rw-r--r--src/mongo/db/views/durable_view_catalog.cpp3
-rw-r--r--src/mongo/db/views/view_catalog.cpp6
-rw-r--r--src/mongo/db/views/view_catalog.h6
12 files changed, 181 insertions, 20 deletions
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index b080647e78d..5f79c148a09 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -58,19 +58,15 @@
#include "mongo/util/decorable.h"
namespace mongo {
+
class CappedCallback;
class CollectionPtr;
-class ExtentManager;
class IndexCatalog;
class IndexCatalogEntry;
-class IndexDescriptor;
-class DatabaseImpl;
class MatchExpression;
class OpDebug;
class OperationContext;
class RecordCursor;
-class UpdateDriver;
-class UpdateRequest;
/**
* Holds information update an update operation.
diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h
index 890ad037de1..32e422b2e0c 100644
--- a/src/mongo/db/catalog/database_holder.h
+++ b/src/mongo/db/catalog/database_holder.h
@@ -42,6 +42,7 @@ class CollectionCatalogEntry;
class Database;
class OperationContext;
class RecordStore;
+class ViewCatalog;
/**
* Registry of opened databases.
@@ -65,6 +66,18 @@ public:
virtual Database* getDb(OperationContext* const opCtx, const StringData ns) const = 0;
/**
+ * Fetches the ViewCatalog decorating the Database matching 'dbName', or returns nullptr if the
+ * database does not exist. The returned ViewCatalog is safe to access without a lock because it
+ * is held as a shared_ptr.
+ *
+ * The ViewCatalog must be fetched through this interface if the caller holds no database lock
+ * to ensure the Database object is safe to access. This class' internal mutex provides
+ * concurrency protection around looking up and accessing the Database object matching 'dbName.
+ */
+ virtual std::shared_ptr<ViewCatalog> getSharedViewCatalog(OperationContext* const opCtx,
+ StringData dbName) const = 0;
+
+ /**
* Retrieves a database reference if it is already opened, or opens it if it hasn't been
* opened/created yet. Must be called with the database locked in X-mode.
*
diff --git a/src/mongo/db/catalog/database_holder_impl.cpp b/src/mongo/db/catalog/database_holder_impl.cpp
index 0fc3fdeb8dc..962504982a6 100644
--- a/src/mongo/db/catalog/database_holder_impl.cpp
+++ b/src/mongo/db/catalog/database_holder_impl.cpp
@@ -43,6 +43,7 @@
#include "mongo/db/service_context.h"
#include "mongo/db/stats/top.h"
#include "mongo/db/storage/storage_engine.h"
+#include "mongo/db/views/view_catalog.h"
#include "mongo/logv2/log.h"
namespace mongo {
@@ -67,7 +68,6 @@ StringData _todb(StringData ns) {
} // namespace
-
Database* DatabaseHolderImpl::getDb(OperationContext* opCtx, StringData ns) const {
const StringData db = _todb(ns);
invariant(opCtx->lockState()->isDbLockedForMode(db, MODE_IS) ||
@@ -82,6 +82,17 @@ Database* DatabaseHolderImpl::getDb(OperationContext* opCtx, StringData ns) cons
return nullptr;
}
+std::shared_ptr<ViewCatalog> DatabaseHolderImpl::getSharedViewCatalog(OperationContext* opCtx,
+ StringData dbName) const {
+ stdx::lock_guard<SimpleMutex> lk(_m);
+ DBs::const_iterator it = _dbs.find(dbName);
+ if (it != _dbs.end()) {
+ return ViewCatalog::getShared(it->second);
+ }
+
+ return nullptr;
+}
+
std::set<std::string> DatabaseHolderImpl::_getNamesWithConflictingCasing_inlock(StringData name) {
std::set<std::string> duplicates;
diff --git a/src/mongo/db/catalog/database_holder_impl.h b/src/mongo/db/catalog/database_holder_impl.h
index 8b200c22a51..b97c0502cdc 100644
--- a/src/mongo/db/catalog/database_holder_impl.h
+++ b/src/mongo/db/catalog/database_holder_impl.h
@@ -42,6 +42,9 @@ public:
Database* getDb(OperationContext* opCtx, StringData ns) const override;
+ std::shared_ptr<ViewCatalog> getSharedViewCatalog(OperationContext* const opCtx,
+ StringData dbName) const override;
+
Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated = nullptr) override;
void dropDb(OperationContext* opCtx, Database* db) override;
diff --git a/src/mongo/db/catalog/database_holder_mock.h b/src/mongo/db/catalog/database_holder_mock.h
index 7aaa86f530f..27a2d0c204c 100644
--- a/src/mongo/db/catalog/database_holder_mock.h
+++ b/src/mongo/db/catalog/database_holder_mock.h
@@ -41,6 +41,11 @@ public:
return nullptr;
}
+ std::shared_ptr<ViewCatalog> getSharedViewCatalog(OperationContext* const opCtx,
+ StringData dbName) const override {
+ return nullptr;
+ }
+
Database* openDb(OperationContext* opCtx, StringData ns, bool* justCreated = nullptr) override {
return nullptr;
}
diff --git a/src/mongo/db/catalog_raii.cpp b/src/mongo/db/catalog_raii.cpp
index f19b9738453..2e1c62ad736 100644
--- a/src/mongo/db/catalog_raii.cpp
+++ b/src/mongo/db/catalog_raii.cpp
@@ -194,6 +194,43 @@ Collection* AutoGetCollection::getWritableCollection(CollectionCatalog::Lifetime
return _writableColl;
}
+AutoGetCollectionLockFree::AutoGetCollectionLockFree(OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollectionViewMode viewMode,
+ Date_t deadline)
+ : _globalLock(
+ opCtx, MODE_IS, deadline, Lock::InterruptBehavior::kThrow, true /* skipRSTLLock */) {
+
+ // Wait for a configured amount of time after acquiring locks if the failpoint is enabled
+ setAutoGetCollectionWait.execute(
+ [&](const BSONObj& data) { sleepFor(Milliseconds(data["waitForMillis"].numberInt())); });
+
+ _resolvedNss = CollectionCatalog::get(opCtx).resolveNamespaceStringOrUUID(opCtx, nsOrUUID);
+ _collection =
+ CollectionCatalog::get(opCtx).lookupCollectionByNamespaceForRead(opCtx, _resolvedNss);
+ _collectionPtr = CollectionPtr(_collection);
+
+ // TODO (SERVER-51319): add DatabaseShardingState::checkDbVersion somewhere.
+
+ if (_collection) {
+ // If the collection exists, there is no need to check for views.
+ return;
+ }
+
+ // Returns nullptr for 'viewCatalog' if db does not exist.
+ auto viewCatalog = DatabaseHolder::get(opCtx)->getSharedViewCatalog(opCtx, _resolvedNss.db());
+ if (!viewCatalog) {
+ return;
+ }
+
+ // TODO (SERVER-51320): this code will invariant in ViewCatalog::lookup() because it takes a
+ // CollectionLock that expects a DBLock to be held on the database already.
+ _view = viewCatalog->lookup(opCtx, _resolvedNss.ns());
+ uassert(ErrorCodes::CommandNotSupportedOnView,
+ str::stream() << "Namespace " << _resolvedNss.ns() << " is a view, not a collection",
+ !_view || viewMode == AutoGetCollectionViewMode::kViewsPermitted);
+}
+
struct CollectionWriter::SharedImpl {
SharedImpl(CollectionWriter* parent) : _parent(parent) {}
diff --git a/src/mongo/db/catalog_raii.h b/src/mongo/db/catalog_raii.h
index eade7f99c4b..d5c499c4bc7 100644
--- a/src/mongo/db/catalog_raii.h
+++ b/src/mongo/db/catalog_raii.h
@@ -198,6 +198,71 @@ protected:
};
/**
+ * RAII-style class that acquires the global MODE_IS lock. This class should only be used for reads.
+ *
+ * NOTE: Throws NamespaceNotFound if the collection UUID cannot be resolved to a nss.
+ *
+ * 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.
+ */
+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());
+
+ /**
+ * AutoGetCollectionLockFree can be used as a Collection pointer with the -> operator.
+ */
+ const Collection* operator->() const {
+ return getCollection().get();
+ }
+
+ /**
+ * Returns nullptr if the collection didn't exist.
+ */
+ const CollectionPtr& getCollection() const {
+ return _collectionPtr;
+ }
+
+ /**
+ * Returns nullptr if the view didn't exist.
+ */
+ ViewDefinition* getView() const {
+ return _view.get();
+ }
+
+ /**
+ * Returns the resolved namespace of the collection or view.
+ */
+ const NamespaceString& getNss() const {
+ return _resolvedNss;
+ }
+
+private:
+ Lock::GlobalLock _globalLock;
+
+ // If the object was instantiated with a UUID, contains the resolved namespace, otherwise it is
+ // the same as the input namespace string
+ NamespaceString _resolvedNss;
+
+ // The Collection shared_ptr will keep the Collection instance alive even if it is removed from
+ // the CollectionCatalog while this lock-free operation runs.
+ std::shared_ptr<const Collection> _collection;
+
+ // The CollectionPtr is the access point to the Collection instance for callers.
+ CollectionPtr _collectionPtr;
+
+ std::shared_ptr<ViewDefinition> _view;
+};
+
+/**
* RAII-style class to handle the lifetime of writable Collections.
* It does not take any locks, concurrency needs to be handled separately using explicit locks or
* AutoGetCollection. This class can serve as an adaptor to unify different methods of acquiring a
diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp
index a48d176689e..ea4738e55aa 100644
--- a/src/mongo/db/concurrency/d_concurrency.cpp
+++ b/src/mongo/db/concurrency/d_concurrency.cpp
@@ -139,11 +139,13 @@ bool Lock::ResourceMutex::isAtLeastReadLocked(Locker* locker) {
Lock::GlobalLock::GlobalLock(OperationContext* opCtx,
LockMode lockMode,
Date_t deadline,
- InterruptBehavior behavior)
+ InterruptBehavior behavior,
+ bool skipRSTLLock)
: _opCtx(opCtx),
_result(LOCK_INVALID),
_pbwm(opCtx->lockState(), resourceIdParallelBatchWriterMode),
_interruptBehavior(behavior),
+ _skipRSTLLock(skipRSTLLock),
_isOutermostLock(!opCtx->lockState()->isLocked()) {
_opCtx->lockState()->getFlowControlTicket(_opCtx, lockMode);
@@ -157,17 +159,14 @@ Lock::GlobalLock::GlobalLock(OperationContext* opCtx,
}
});
- _opCtx->lockState()->lock(
- _opCtx, resourceIdReplicationStateTransitionLock, MODE_IX, deadline);
-
- auto unlockRSTL = makeGuard(
- [this] { _opCtx->lockState()->unlock(resourceIdReplicationStateTransitionLock); });
-
_result = LOCK_INVALID;
- _opCtx->lockState()->lockGlobal(_opCtx, lockMode, deadline);
+ if (skipRSTLLock) {
+ _takeGlobalLockOnly(lockMode, deadline);
+ } else {
+ _takeGlobalAndRSTLLocks(lockMode, deadline);
+ }
_result = LOCK_OK;
- unlockRSTL.dismiss();
unlockPBWM.dismiss();
} catch (const ExceptionForCat<ErrorCategory::Interruption>&) {
// The kLeaveUnlocked behavior suppresses this exception.
@@ -178,11 +177,26 @@ Lock::GlobalLock::GlobalLock(OperationContext* opCtx,
_opCtx->lockState()->setGlobalLockTakenInMode(acquiredLockMode);
}
+void Lock::GlobalLock::_takeGlobalLockOnly(LockMode lockMode, Date_t deadline) {
+ _opCtx->lockState()->lockGlobal(_opCtx, lockMode, deadline);
+}
+
+void Lock::GlobalLock::_takeGlobalAndRSTLLocks(LockMode lockMode, Date_t deadline) {
+ _opCtx->lockState()->lock(_opCtx, resourceIdReplicationStateTransitionLock, MODE_IX, deadline);
+ auto unlockRSTL = makeGuard(
+ [this] { _opCtx->lockState()->unlock(resourceIdReplicationStateTransitionLock); });
+
+ _opCtx->lockState()->lockGlobal(_opCtx, lockMode, deadline);
+
+ unlockRSTL.dismiss();
+}
+
Lock::GlobalLock::GlobalLock(GlobalLock&& otherLock)
: _opCtx(otherLock._opCtx),
_result(otherLock._result),
_pbwm(std::move(otherLock._pbwm)),
_interruptBehavior(otherLock._interruptBehavior),
+ _skipRSTLLock(otherLock._skipRSTLLock),
_isOutermostLock(otherLock._isOutermostLock) {
// Mark as moved so the destructor doesn't invalidate the newly-constructed lock.
otherLock._result = LOCK_INVALID;
diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h
index 187cb3d5a41..4ae7af8cdb9 100644
--- a/src/mongo/db/concurrency/d_concurrency.h
+++ b/src/mongo/db/concurrency/d_concurrency.h
@@ -221,7 +221,8 @@ public:
GlobalLock(OperationContext* opCtx,
LockMode lockMode,
Date_t deadline,
- InterruptBehavior behavior);
+ InterruptBehavior behavior,
+ bool skipRSTLLock = false);
GlobalLock(GlobalLock&&);
@@ -239,7 +240,7 @@ public:
}
_unlock();
}
- if (lockResult == LOCK_OK || lockResult == LOCK_WAITING) {
+ if (!_skipRSTLLock && (lockResult == LOCK_OK || lockResult == LOCK_WAITING)) {
_opCtx->lockState()->unlock(resourceIdReplicationStateTransitionLock);
}
}
@@ -249,12 +250,19 @@ public:
}
private:
+ /**
+ * Constructor helper functions, to handle skipping or taking the RSTL lock.
+ */
+ void _takeGlobalLockOnly(LockMode lockMode, Date_t deadline);
+ void _takeGlobalAndRSTLLocks(LockMode lockMode, Date_t deadline);
+
void _unlock();
OperationContext* const _opCtx;
LockResult _result;
ResourceLock _pbwm;
InterruptBehavior _interruptBehavior;
+ bool _skipRSTLLock;
const bool _isOutermostLock;
};
diff --git a/src/mongo/db/views/durable_view_catalog.cpp b/src/mongo/db/views/durable_view_catalog.cpp
index 25b0c2a28a6..838d883fc0d 100644
--- a/src/mongo/db/views/durable_view_catalog.cpp
+++ b/src/mongo/db/views/durable_view_catalog.cpp
@@ -86,8 +86,7 @@ void DurableViewCatalog::onSystemViewsCollectionDrop(OperationContext* opCtx,
if (db) {
// If the 'system.views' collection is dropped, we need to clear the in-memory state of the
// view catalog.
- ViewCatalog* viewCatalog = ViewCatalog::get(db);
- viewCatalog->clear();
+ ViewCatalog::get(db)->clear();
}
}
diff --git a/src/mongo/db/views/view_catalog.cpp b/src/mongo/db/views/view_catalog.cpp
index d816f520460..2ed8ac9ee17 100644
--- a/src/mongo/db/views/view_catalog.cpp
+++ b/src/mongo/db/views/view_catalog.cpp
@@ -60,7 +60,7 @@
namespace mongo {
namespace {
-auto getViewCatalog = Database::declareDecoration<std::unique_ptr<ViewCatalog>>();
+auto getViewCatalog = Database::declareDecoration<std::shared_ptr<ViewCatalog>>();
StatusWith<std::unique_ptr<CollatorInterface>> parseCollator(OperationContext* opCtx,
BSONObj collationSpec) {
@@ -73,6 +73,10 @@ StatusWith<std::unique_ptr<CollatorInterface>> parseCollator(OperationContext* o
}
} // namespace
+std::shared_ptr<ViewCatalog> ViewCatalog::getShared(const Database* db) {
+ return getViewCatalog(db);
+}
+
ViewCatalog* ViewCatalog::get(const Database* db) {
return getViewCatalog(db).get();
}
diff --git a/src/mongo/db/views/view_catalog.h b/src/mongo/db/views/view_catalog.h
index 44103a0ed24..ecfff9de990 100644
--- a/src/mongo/db/views/view_catalog.h
+++ b/src/mongo/db/views/view_catalog.h
@@ -67,6 +67,12 @@ public:
using ViewMap = StringMap<std::shared_ptr<ViewDefinition>>;
using ViewIteratorCallback = std::function<void(const ViewDefinition& view)>;
+ /**
+ * This getter should only be used when not holding a database lock. Otherwise the regular get()
+ * is appropriate and safe.
+ */
+ static std::shared_ptr<ViewCatalog> getShared(const Database* db);
+
static ViewCatalog* get(const Database* db);
static void set(Database* db, std::unique_ptr<ViewCatalog> catalog);