summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-07-12 09:50:46 -0400
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-07-15 10:47:12 -0400
commit71fced4ef1bdbc1e5b517057eb15be256eaf0ba7 (patch)
treeeaa12252283a4702203f9865796a73ede5b011b3
parent903207938dc05f9e3f4ca546232d8a7ceda99e4c (diff)
downloadmongo-71fced4ef1bdbc1e5b517057eb15be256eaf0ba7.tar.gz
SERVER-41041 ViewCatalog should actively reload after changes
-rw-r--r--src/mongo/db/catalog/database_impl.cpp11
-rw-r--r--src/mongo/db/op_observer_impl.cpp2
-rw-r--r--src/mongo/db/s/set_shard_version_command.cpp5
-rw-r--r--src/mongo/db/s/shard_filtering_metadata_refresh.cpp24
-rw-r--r--src/mongo/db/views/SConscript2
-rw-r--r--src/mongo/db/views/durable_view_catalog.cpp31
-rw-r--r--src/mongo/db/views/durable_view_catalog.h8
-rw-r--r--src/mongo/db/views/view_catalog.cpp103
-rw-r--r--src/mongo/db/views/view_catalog.h56
-rw-r--r--src/mongo/db/views/view_catalog_test.cpp394
10 files changed, 364 insertions, 272 deletions
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 0b537c99e2e..1388714f3f7 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -147,13 +147,12 @@ void DatabaseImpl::init(OperationContext* const opCtx) const {
}
// At construction time of the viewCatalog, the CollectionCatalog map wasn't initialized yet,
- // so no system.views collection would be found. Now we're sufficiently initialized, signal a
- // version change. Also force a reload, so if there are problems with the catalog contents as
- // might be caused by incorrect mongod versions or similar, they are found right away.
+ // so no system.views collection would be found. Now that we're sufficiently initialized, reload
+ // the viewCatalog to populate its in-memory state. If there are problems with the catalog
+ // contents as might be caused by incorrect mongod versions or similar, they are found right
+ // away.
auto views = ViewCatalog::get(this);
- views->invalidate();
- Status reloadStatus = views->reloadIfNeeded(opCtx);
-
+ Status reloadStatus = views->reload(opCtx, ViewCatalogLookupBehavior::kValidateDurableViews);
if (!reloadStatus.isOK()) {
warning() << "Unable to parse views: " << redact(reloadStatus)
<< "; remove any invalid views from the " << _viewsName
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index 0e08c5f1065..870c10a7c1f 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -664,7 +664,7 @@ repl::OpTime OpObserverImpl::onDropCollection(OperationContext* opCtx,
collectionName != NamespaceString::kServerConfigurationNamespace);
if (collectionName.coll() == DurableViewCatalog::viewsCollectionName()) {
- DurableViewCatalog::onExternalChange(opCtx, collectionName);
+ DurableViewCatalog::onSystemViewsCollectionDrop(opCtx, collectionName);
} else if (collectionName == NamespaceString::kSessionTransactionsTableNamespace) {
MongoDSessionCatalog::invalidateAllSessions(opCtx);
}
diff --git a/src/mongo/db/s/set_shard_version_command.cpp b/src/mongo/db/s/set_shard_version_command.cpp
index 8db4f26f347..dd03e31b206 100644
--- a/src/mongo/db/s/set_shard_version_command.cpp
+++ b/src/mongo/db/s/set_shard_version_command.cpp
@@ -350,7 +350,10 @@ public:
opCtx, nss, requestedVersion, forceRefresh /*forceRefreshFromThisThread*/);
{
- AutoGetCollection autoColl(opCtx, nss, MODE_IS);
+ // Avoid using AutoGetCollection() as it returns the InvalidViewDefinition error code
+ // if an invalid view is in the 'system.views' collection.
+ AutoGetDb autoDb(opCtx, nss.db(), MODE_IS);
+ Lock::CollectionLock collLock(opCtx, nss, MODE_IS);
const ChunkVersion currVersion = [&] {
auto* const css = CollectionShardingState::get(opCtx, nss);
diff --git a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp
index fe3790eaffd..cab3a7391fd 100644
--- a/src/mongo/db/s/shard_filtering_metadata_refresh.cpp
+++ b/src/mongo/db/s/shard_filtering_metadata_refresh.cpp
@@ -74,7 +74,10 @@ void onShardVersionMismatch(OperationContext* opCtx,
OperationShardingState::get(opCtx).waitForMigrationCriticalSectionSignal(opCtx);
const auto currentShardVersion = [&] {
- AutoGetCollection autoColl(opCtx, nss, MODE_IS);
+ // Avoid using AutoGetCollection() as it returns the InvalidViewDefinition error code
+ // if an invalid view is in the 'system.views' collection.
+ AutoGetDb autoDb(opCtx, nss.db(), MODE_IS);
+ Lock::CollectionLock collLock(opCtx, nss, MODE_IS);
return CollectionShardingState::get(opCtx, nss)->getCurrentShardVersionIfKnown();
}();
@@ -151,9 +154,10 @@ ChunkVersion forceShardFilteringMetadataRefresh(OperationContext* opCtx,
auto cm = routingInfo.cm();
if (!cm) {
- // No chunk manager, so unsharded.
-
- AutoGetCollection autoColl(opCtx, nss, MODE_IX);
+ // No chunk manager, so unsharded. Avoid using AutoGetCollection() as it returns the
+ // InvalidViewDefinition error code if an invalid view is in the 'system.views' collection.
+ AutoGetDb autoDb(opCtx, nss.db(), MODE_IX);
+ Lock::CollectionLock collLock(opCtx, nss, MODE_IX);
CollectionShardingRuntime::get(opCtx, nss)
->setFilteringMetadata(opCtx, CollectionMetadata());
@@ -163,7 +167,10 @@ ChunkVersion forceShardFilteringMetadataRefresh(OperationContext* opCtx,
// Optimistic check with only IS lock in order to avoid threads piling up on the collection X
// lock below
{
- AutoGetCollection autoColl(opCtx, nss, MODE_IS);
+ // Avoid using AutoGetCollection() as it returns the InvalidViewDefinition error code
+ // if an invalid view is in the 'system.views' collection.
+ AutoGetDb autoDb(opCtx, nss.db(), MODE_IS);
+ Lock::CollectionLock collLock(opCtx, nss, MODE_IS);
auto optMetadata = CollectionShardingState::get(opCtx, nss)->getCurrentMetadataIfKnown();
// We already have newer version
@@ -179,8 +186,11 @@ ChunkVersion forceShardFilteringMetadataRefresh(OperationContext* opCtx,
}
}
- // Exclusive collection lock needed since we're now changing the metadata
- AutoGetCollection autoColl(opCtx, nss, MODE_IX);
+ // Exclusive collection lock needed since we're now changing the metadata. Avoid using
+ // AutoGetCollection() as it returns the InvalidViewDefinition error code if an invalid view is
+ // in the 'system.views' collection.
+ AutoGetDb autoDb(opCtx, nss.db(), MODE_IX);
+ Lock::CollectionLock collLock(opCtx, nss, MODE_IX);
auto* const css = CollectionShardingRuntime::get(opCtx, nss);
{
diff --git a/src/mongo/db/views/SConscript b/src/mongo/db/views/SConscript
index e4b769b075a..00aba3711cb 100644
--- a/src/mongo/db/views/SConscript
+++ b/src/mongo/db/views/SConscript
@@ -54,7 +54,9 @@ env.CppUnitTest(
],
LIBDEPS=[
'views',
+ 'views_mongod',
'$BUILD_DIR/mongo/db/auth/authmocks',
+ '$BUILD_DIR/mongo/db/catalog/catalog_test_fixture',
'$BUILD_DIR/mongo/db/query/collation/collator_interface_mock',
'$BUILD_DIR/mongo/db/query/query_test_service_context',
'$BUILD_DIR/mongo/db/repl/replmocks',
diff --git a/src/mongo/db/views/durable_view_catalog.cpp b/src/mongo/db/views/durable_view_catalog.cpp
index 30206b54d91..193f7a6f432 100644
--- a/src/mongo/db/views/durable_view_catalog.cpp
+++ b/src/mongo/db/views/durable_view_catalog.cpp
@@ -56,11 +56,38 @@ namespace mongo {
void DurableViewCatalog::onExternalChange(OperationContext* opCtx, const NamespaceString& name) {
dassert(opCtx->lockState()->isDbLockedForMode(name.db(), MODE_IX));
+ dassert(opCtx->lockState()->isCollectionLockedForMode(
+ NamespaceString(name.db(), NamespaceString::kSystemDotViewsCollectionName), MODE_X));
auto databaseHolder = DatabaseHolder::get(opCtx);
auto db = databaseHolder->getDb(opCtx, name.db());
if (db) {
- opCtx->recoveryUnit()->onCommit(
- [db](boost::optional<Timestamp>) { ViewCatalog::get(db)->invalidate(); });
+ // On an external change, an invalid view definition can be detected when the view catalog
+ // is reloaded. This will prevent any further usage of the view catalog until the invalid
+ // view definitions are removed. We use kValidateDurableViews here to catch any invalid view
+ // definitions in the view catalog to make it unusable for subsequent callers.
+ ViewCatalog* viewCatalog = ViewCatalog::get(db);
+ if (viewCatalog->shouldIgnoreExternalChange(opCtx, name)) {
+ return;
+ }
+
+ viewCatalog->reload(opCtx, ViewCatalogLookupBehavior::kValidateDurableViews).ignore();
+ }
+}
+
+void DurableViewCatalog::onSystemViewsCollectionDrop(OperationContext* opCtx,
+ const NamespaceString& name) {
+ dassert(opCtx->lockState()->isDbLockedForMode(name.db(), MODE_IX));
+ dassert(opCtx->lockState()->isCollectionLockedForMode(
+ NamespaceString(name.db(), NamespaceString::kSystemDotViewsCollectionName), MODE_X));
+ dassert(name.coll() == NamespaceString::kSystemDotViewsCollectionName);
+
+ auto databaseHolder = DatabaseHolder::get(opCtx);
+ auto db = databaseHolder->getDb(opCtx, name.db());
+ 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();
}
}
diff --git a/src/mongo/db/views/durable_view_catalog.h b/src/mongo/db/views/durable_view_catalog.h
index 03bacb2f3fb..62fd5db2c4d 100644
--- a/src/mongo/db/views/durable_view_catalog.h
+++ b/src/mongo/db/views/durable_view_catalog.h
@@ -63,10 +63,16 @@ public:
/**
* Thread-safe method to mark a catalog name was changed. This will cause the in-memory
- * view catalog to be marked invalid
+ * view catalog to be reloaded immediately.
*/
static void onExternalChange(OperationContext* opCtx, const NamespaceString& name);
+ /**
+ * Thread-safe method to clear the in-memory state of the view catalog when the 'system.views'
+ * collection is dropped.
+ */
+ static void onSystemViewsCollectionDrop(OperationContext* opCtx, const NamespaceString& name);
+
using Callback = std::function<Status(const BSONObj& view)>;
virtual void iterate(OperationContext* opCtx, Callback callback) = 0;
virtual void iterateIgnoreInvalidEntries(OperationContext* opCtx, Callback callback) = 0;
diff --git a/src/mongo/db/views/view_catalog.cpp b/src/mongo/db/views/view_catalog.cpp
index 2de991d959e..237a9495cf2 100644
--- a/src/mongo/db/views/view_catalog.cpp
+++ b/src/mongo/db/views/view_catalog.cpp
@@ -82,25 +82,23 @@ void ViewCatalog::set(Database* db, std::unique_ptr<ViewCatalog> catalog) {
getViewCatalog(db) = std::move(catalog);
}
-Status ViewCatalog::reloadIfNeeded(OperationContext* opCtx) {
+Status ViewCatalog::reload(OperationContext* opCtx, ViewCatalogLookupBehavior lookupBehavior) {
Lock::CollectionLock systemViewsLock(
opCtx,
NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName),
MODE_IS);
stdx::unique_lock<stdx::mutex> lk(_mutex);
- return _reloadIfNeeded(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews);
+ return _reload(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews);
}
-Status ViewCatalog::_reloadIfNeeded(WithLock lk,
- OperationContext* opCtx,
- ViewCatalogLookupBehavior lookupBehavior) {
- if (_valid.load())
- return Status::OK();
-
+Status ViewCatalog::_reload(WithLock,
+ OperationContext* opCtx,
+ ViewCatalogLookupBehavior lookupBehavior) {
LOG(1) << "reloading view catalog for database " << _durable->getName();
- // Need to reload, first clear our cache.
_viewMap.clear();
+ _valid = false;
+ _viewGraphNeedsRefresh = true;
auto reloadCallback = [&](const BSONObj& view) -> Status {
BSONObj collationSpec = view.hasField("collation") ? view["collation"].Obj() : BSONObj();
@@ -145,17 +143,38 @@ Status ViewCatalog::_reloadIfNeeded(WithLock lk,
return status;
}
- _valid.store(true);
+ _valid = true;
return Status::OK();
}
+void ViewCatalog::clear() {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+
+ _viewMap.clear();
+ _viewGraph.clear();
+ _valid = true;
+ _viewGraphNeedsRefresh = false;
+}
+
+bool ViewCatalog::shouldIgnoreExternalChange(OperationContext* opCtx,
+ const NamespaceString& name) const {
+ return _ignoreExternalChange;
+}
+
+void ViewCatalog::_requireValidCatalog(WithLock) {
+ uassert(ErrorCodes::InvalidViewDefinition,
+ "Invalid view definition detected in the view catalog. Remove the invalid view "
+ "manually to prevent disallowing any further usage of the view catalog.",
+ _valid);
+}
+
void ViewCatalog::iterate(OperationContext* opCtx, ViewIteratorCallback callback) {
Lock::CollectionLock systemViewsLock(
opCtx,
NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName),
MODE_IS);
stdx::lock_guard<stdx::mutex> lk(_mutex);
- uassertStatusOK(_reloadIfNeeded(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews));
+ _requireValidCatalog(lk);
for (auto&& view : _viewMap) {
callback(*view.second);
}
@@ -172,7 +191,11 @@ Status ViewCatalog::_createOrUpdateView(WithLock lk,
invariant(opCtx->lockState()->isCollectionLockedForMode(
NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName), MODE_X));
- _requireValidCatalog(lk, opCtx);
+ _requireValidCatalog(lk);
+
+ ON_BLOCK_EXIT([this] { _ignoreExternalChange = false; });
+
+ _ignoreExternalChange = true;
// Build the BSON definition for this view to be saved in the durable view catalog. If the
// collation is empty, omit it from the definition altogether.
@@ -209,10 +232,8 @@ Status ViewCatalog::_createOrUpdateView(WithLock lk,
catalog.removeResource(viewRid, viewName.ns());
});
- // We may get invalidated, but we're exclusively locked, so the change must be ours.
- opCtx->recoveryUnit()->onCommit(
- [this](boost::optional<Timestamp>) { this->_valid.store(true); });
- return Status::OK();
+ // Reload the view catalog with the changes applied.
+ return _reload(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews);
}
Status ViewCatalog::_upsertIntoGraph(WithLock lk,
@@ -364,7 +385,6 @@ Status ViewCatalog::createView(OperationContext* opCtx,
const NamespaceString& viewOn,
const BSONArray& pipeline,
const BSONObj& collation) {
-
invariant(opCtx->lockState()->isDbLockedForMode(viewName.db(), MODE_IX));
invariant(opCtx->lockState()->isCollectionLockedForMode(viewName, MODE_IX));
invariant(opCtx->lockState()->isCollectionLockedForMode(
@@ -443,7 +463,11 @@ Status ViewCatalog::dropView(OperationContext* opCtx, const NamespaceString& vie
NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName), MODE_X));
stdx::lock_guard<stdx::mutex> lk(_mutex);
- _requireValidCatalog(lk, opCtx);
+ _requireValidCatalog(lk);
+
+ ON_BLOCK_EXIT([this] { _ignoreExternalChange = false; });
+
+ _ignoreExternalChange = true;
// Save a copy of the view definition in case we need to roll back.
auto viewPtr =
@@ -455,7 +479,7 @@ Status ViewCatalog::dropView(OperationContext* opCtx, const NamespaceString& vie
ViewDefinition savedDefinition = *viewPtr;
- invariant(_valid.load());
+ invariant(_valid);
_durable->remove(opCtx, viewName);
_viewGraph.remove(savedDefinition.name());
_viewMap.erase(viewName.ns());
@@ -471,30 +495,14 @@ Status ViewCatalog::dropView(OperationContext* opCtx, const NamespaceString& vie
catalog.addResource(viewRid, viewName.ns());
});
- // We may get invalidated, but we're exclusively locked, so the change must be ours.
- opCtx->recoveryUnit()->onCommit(
- [this](boost::optional<Timestamp>) { this->_valid.store(true); });
- return Status::OK();
+ // Reload the view catalog with the changes applied.
+ return _reload(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews);
}
std::shared_ptr<ViewDefinition> ViewCatalog::_lookup(WithLock lk,
OperationContext* opCtx,
StringData ns,
ViewCatalogLookupBehavior lookupBehavior) {
- // We expect the catalog to be valid, so short-circuit other checks for best performance.
- if (MONGO_unlikely(!_valid.load())) {
- // If the catalog is invalid, we want to avoid references to virtualized or other invalid
- // collection names to trigger a reload. This makes the system more robust in presence of
- // invalid view definitions.
- if (!NamespaceString::validCollectionName(ns))
- return nullptr;
- Status status = _reloadIfNeeded(lk, opCtx, lookupBehavior);
- // In case of errors we've already logged a message. Only uassert if there actually is
- // a user connection, as otherwise we'd crash the server. The catalog will remain invalid,
- // and any views after the first invalid one are ignored.
- if (opCtx->getClient()->isFromUserConnection())
- uassertStatusOK(status);
- }
ViewMap::const_iterator it = _viewMap.find(ns);
if (it != _viewMap.end()) {
@@ -509,6 +517,18 @@ std::shared_ptr<ViewDefinition> ViewCatalog::lookup(OperationContext* opCtx, Str
NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName),
MODE_IS);
stdx::lock_guard<stdx::mutex> lk(_mutex);
+ if (!_valid && opCtx->getClient()->isFromUserConnection()) {
+ // We want to avoid lookups on invalid collection names.
+ if (!NamespaceString::validCollectionName(ns)) {
+ return nullptr;
+ }
+
+ // ApplyOps should work on a valid existing collection, despite the presence of bad views
+ // otherwise the server would crash. The view catalog will remain invalid until the bad view
+ // definitions are removed.
+ _requireValidCatalog(lk);
+ }
+
return _lookup(lk, opCtx, ns, ViewCatalogLookupBehavior::kValidateDurableViews);
}
@@ -530,6 +550,8 @@ StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx,
MODE_IS);
stdx::unique_lock<stdx::mutex> lock(_mutex);
+ _requireValidCatalog(lock);
+
// Keep looping until the resolution completes. If the catalog is invalidated during the
// resolution, we start over from the beginning.
while (true) {
@@ -549,13 +571,6 @@ StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx,
int depth = 0;
for (; depth < ViewGraph::kMaxViewDepth; depth++) {
- // If the catalog has been invalidated, bail and restart.
- if (!_valid.load()) {
- uassertStatusOK(
- _reloadIfNeeded(lock, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews));
- break;
- }
-
auto view = _lookup(
lock, opCtx, resolvedNss->ns(), ViewCatalogLookupBehavior::kValidateDurableViews);
if (!view) {
diff --git a/src/mongo/db/views/view_catalog.h b/src/mongo/db/views/view_catalog.h
index 8aee3c1b730..a8fd9df9e3e 100644
--- a/src/mongo/db/views/view_catalog.h
+++ b/src/mongo/db/views/view_catalog.h
@@ -71,7 +71,10 @@ public:
static void set(Database* db, std::unique_ptr<ViewCatalog> catalog);
explicit ViewCatalog(std::unique_ptr<DurableViewCatalog> durable)
- : _durable(std::move(durable)) {}
+ : _durable(std::move(durable)),
+ _valid(false),
+ _viewGraphNeedsRefresh(true),
+ _ignoreExternalChange(false) {}
/**
* Iterates through the catalog, applying 'callback' to each view. This callback function
@@ -133,22 +136,25 @@ public:
StatusWith<ResolvedView> resolveView(OperationContext* opCtx, const NamespaceString& nss);
/**
- * Reload the views catalog if marked invalid. No-op if already valid. Does only minimal
- * validation, namely that the view definitions are valid BSON and have no unknown fields.
- * Reading stops on the first invalid entry. Errors are logged and returned. Performs no
- * cycle detection etc. This is implicitly called by other methods when the ViewCatalog is
- * marked invalid, and on first opening a database.
+ * Reloads the in-memory state of the view catalog from the 'system.views' collection catalog.
+ * If the 'lookupBehavior' is 'kValidateDurableViews', then the durable view definitions will be
+ * validated. Reading stops on the first invalid entry with errors logged and returned. Performs
+ * no cycle detection, etc.
+ * This is implicitly called by other methods when write operations are performed on the view
+ * catalog, on external changes to the 'system.views' collection and on the first opening of a
+ * database.
*/
- Status reloadIfNeeded(OperationContext* opCtx);
+ Status reload(OperationContext* opCtx, ViewCatalogLookupBehavior lookupBehavior);
/**
- * To be called when direct modifications to the DurableViewCatalog have been committed, so
- * subsequent lookups will reload the catalog and make the changes visible.
+ * Clears the in-memory state of the view catalog.
*/
- void invalidate() {
- _valid.store(false);
- _viewGraphNeedsRefresh = true;
- }
+ void clear();
+
+ /**
+ * The view catalog needs to ignore external changes for its own modifications.
+ */
+ bool shouldIgnoreExternalChange(OperationContext* opCtx, const NamespaceString& name) const;
private:
Status _createOrUpdateView(WithLock,
@@ -183,21 +189,23 @@ private:
OperationContext* opCtx,
StringData ns,
ViewCatalogLookupBehavior lookupBehavior);
- Status _reloadIfNeeded(WithLock,
- OperationContext* opCtx,
- ViewCatalogLookupBehavior lookupBehavior);
- void _requireValidCatalog(WithLock lk, OperationContext* opCtx) {
- uassertStatusOK(
- _reloadIfNeeded(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews));
- invariant(_valid.load());
- }
+ Status _reload(WithLock, OperationContext* opCtx, ViewCatalogLookupBehavior lookupBehavior);
+
+ /**
+ * uasserts with the InvalidViewDefinition error if the current in-memory state of the view
+ * catalog is invalid. This ensures that calling into the view catalog while it is invalid
+ * renders it inoperable.
+ */
+ void _requireValidCatalog(WithLock);
- stdx::mutex _mutex; // Protects all members, except for _valid.
+ stdx::mutex _mutex; // Protects all members.
ViewMap _viewMap;
+ ViewMap _viewMapBackup;
std::unique_ptr<DurableViewCatalog> _durable;
- AtomicWord<bool> _valid;
+ bool _valid;
ViewGraph _viewGraph;
- bool _viewGraphNeedsRefresh = true; // Defers initializing the graph until the first insert.
+ bool _viewGraphNeedsRefresh;
+ bool _ignoreExternalChange;
};
} // namespace mongo
diff --git a/src/mongo/db/views/view_catalog_test.cpp b/src/mongo/db/views/view_catalog_test.cpp
index 6737a38b6a1..87aa9340ba8 100644
--- a/src/mongo/db/views/view_catalog_test.cpp
+++ b/src/mongo/db/views/view_catalog_test.cpp
@@ -39,7 +39,9 @@
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/catalog/catalog_test_fixture.h"
#include "mongo/db/catalog/collection_catalog.h"
+#include "mongo/db/catalog_raii.h"
#include "mongo/db/concurrency/lock_manager_defs.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
@@ -74,61 +76,103 @@ constexpr auto kLargeString =
const auto kOneKiBMatchStage = BSON("$match" << BSON("data" << kLargeString));
const auto kTinyMatchStage = BSON("$match" << BSONObj());
-class DurableViewCatalogDummy final : public DurableViewCatalog {
+class ViewCatalogFixture : public CatalogTestFixture {
public:
- explicit DurableViewCatalogDummy() : _upsertCount(0), _iterateCount(0) {}
- static const std::string name;
+ void setUp() override {
+ CatalogTestFixture::setUp();
+
+ WriteUnitOfWork wuow(operationContext());
+ AutoGetOrCreateDb autoDb(operationContext(), "db", MODE_X);
+ _db = autoDb.getDb();
+ invariant(_db);
+
+ auto durableViewCatalogUnique = std::make_unique<DurableViewCatalogImpl>(_db);
+ durableViewCatalog = durableViewCatalogUnique.get();
- using Callback = std::function<Status(const BSONObj& view)>;
- virtual void iterate(OperationContext* opCtx, Callback callback) {
- ++_iterateCount;
+ _viewCatalog = ViewCatalog::get(_db);
+
+ // Create the system views collection for the database.
+ ASSERT(_db->createCollection(
+ operationContext(),
+ NamespaceString("db", NamespaceString::kSystemDotViewsCollectionName)));
+
+ // Create any additional databases used throughout the test.
+ ASSERT(AutoGetOrCreateDb(operationContext(), "db1", MODE_X).getDb());
+ ASSERT(AutoGetOrCreateDb(operationContext(), "db2", MODE_X).getDb());
+ wuow.commit();
}
- virtual void iterateIgnoreInvalidEntries(OperationContext* opCtx, Callback callback) {
- ++_iterateCount;
+
+ void tearDown() {
+ CatalogTestFixture::tearDown();
}
- virtual void upsert(OperationContext* opCtx, const NamespaceString& name, const BSONObj& view) {
- ++_upsertCount;
+
+ ViewCatalog* getViewCatalog() const {
+ return _viewCatalog;
}
- virtual void remove(OperationContext* opCtx, const NamespaceString& name) {}
- virtual const std::string& getName() const {
- return name;
- };
- int getUpsertCount() {
- return _upsertCount;
+ Status createView(OperationContext* opCtx,
+ const NamespaceString& viewName,
+ const NamespaceString& viewOn,
+ const BSONArray& pipeline,
+ const BSONObj& collation) {
+ Lock::DBLock dbLock(operationContext(), viewName.db(), MODE_IX);
+ Lock::CollectionLock collLock(operationContext(), viewName, MODE_IX);
+ Lock::CollectionLock sysCollLock(
+ operationContext(),
+ NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName),
+ MODE_X);
+
+ WriteUnitOfWork wuow(opCtx);
+ Status s = _viewCatalog->createView(opCtx, viewName, viewOn, pipeline, collation);
+ wuow.commit();
+
+ return s;
}
- int getIterateCount() {
- return _iterateCount;
+ Status modifyView(OperationContext* opCtx,
+ const NamespaceString& viewName,
+ const NamespaceString& viewOn,
+ const BSONArray& pipeline) {
+ Lock::DBLock dbLock(operationContext(), viewName.db(), MODE_X);
+ Lock::CollectionLock collLock(operationContext(), viewName, MODE_IX);
+ Lock::CollectionLock sysCollLock(
+ operationContext(),
+ NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName),
+ MODE_X);
+
+ WriteUnitOfWork wuow(opCtx);
+ Status s = _viewCatalog->modifyView(opCtx, viewName, viewOn, pipeline);
+ wuow.commit();
+
+ return s;
}
-private:
- int _upsertCount;
- int _iterateCount;
-};
+ Status dropView(OperationContext* opCtx, const NamespaceString& viewName) {
+ Lock::DBLock dbLock(operationContext(), viewName.db(), MODE_IX);
+ Lock::CollectionLock collLock(operationContext(), viewName, MODE_IX);
+ Lock::CollectionLock sysCollLock(
+ operationContext(),
+ NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName),
+ MODE_X);
-const std::string DurableViewCatalogDummy::name = "dummy";
+ WriteUnitOfWork wuow(opCtx);
+ Status s = _viewCatalog->dropView(opCtx, viewName);
+ wuow.commit();
-class ViewCatalogFixture : public unittest::Test {
-public:
- ViewCatalogFixture()
- : _queryServiceContext(std::make_unique<QueryTestServiceContext>()),
- opCtx(_queryServiceContext->makeOperationContext()),
- viewCatalog(std::move(durableViewCatalogUnique)) {}
+ return s;
+ }
+
+ std::shared_ptr<ViewDefinition> lookup(OperationContext* opCtx, StringData ns) {
+ Lock::DBLock dbLock(operationContext(), NamespaceString(ns).db(), MODE_IS);
+ return _viewCatalog->lookup(operationContext(), ns);
+ }
private:
- std::unique_ptr<QueryTestServiceContext> _queryServiceContext;
+ Database* _db;
+ ViewCatalog* _viewCatalog;
protected:
- ServiceContext* getServiceContext() const {
- return _queryServiceContext->getServiceContext();
- }
-
- std::unique_ptr<DurableViewCatalogDummy> durableViewCatalogUnique =
- std::make_unique<DurableViewCatalogDummy>();
- DurableViewCatalogDummy* durableViewCatalog = durableViewCatalogUnique.get();
- ServiceContext::UniqueOperationContext opCtx;
- ViewCatalog viewCatalog;
+ DurableViewCatalogImpl* durableViewCatalog;
const BSONArray emptyPipeline;
const BSONObj emptyCollation;
};
@@ -137,7 +181,7 @@ protected:
class ReplViewCatalogFixture : public ViewCatalogFixture {
public:
void setUp() override {
- Test::setUp();
+ ViewCatalogFixture::setUp();
auto service = getServiceContext();
repl::ReplSettings settings;
@@ -156,47 +200,44 @@ TEST_F(ViewCatalogFixture, CreateExistingView) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
- ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_NOT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
TEST_F(ViewCatalogFixture, CreateViewOnDifferentDatabase) {
const NamespaceString viewName("db1.view");
const NamespaceString viewOn("db2.coll");
- ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_NOT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
TEST_F(ViewCatalogFixture, CanCreateViewWithExprPredicate) {
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(),
- NamespaceString("db.view1"),
- viewOn,
- BSON_ARRAY(BSON("$match" << BSON("$expr" << 1))),
- emptyCollation));
-
- ASSERT_OK(viewCatalog.createView(
- opCtx.get(),
- NamespaceString("db.view2"),
- viewOn,
- BSON_ARRAY(
- BSON("$facet" << BSON("output" << BSON_ARRAY(BSON("$match" << BSON("$expr" << 1)))))),
- emptyCollation));
+ ASSERT_OK(createView(operationContext(),
+ NamespaceString("db.view1"),
+ viewOn,
+ BSON_ARRAY(BSON("$match" << BSON("$expr" << 1))),
+ emptyCollation));
+
+ ASSERT_OK(createView(operationContext(),
+ NamespaceString("db.view2"),
+ viewOn,
+ BSON_ARRAY(BSON("$facet" << BSON("output" << BSON_ARRAY(BSON(
+ "$match" << BSON("$expr" << 1)))))),
+ emptyCollation));
}
TEST_F(ViewCatalogFixture, CanCreateViewWithJSONSchemaPredicate) {
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(
- opCtx.get(),
+ ASSERT_OK(createView(
+ operationContext(),
NamespaceString("db.view1"),
viewOn,
BSON_ARRAY(BSON("$match" << BSON("$jsonSchema" << BSON("required" << BSON_ARRAY("x"))))),
emptyCollation));
- ASSERT_OK(viewCatalog.createView(
- opCtx.get(),
+ ASSERT_OK(createView(
+ operationContext(),
NamespaceString("db.view2"),
viewOn,
BSON_ARRAY(BSON(
@@ -208,16 +249,16 @@ TEST_F(ViewCatalogFixture, CanCreateViewWithJSONSchemaPredicate) {
TEST_F(ViewCatalogFixture, CanCreateViewWithLookupUsingPipelineSyntax) {
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(),
- NamespaceString("db.view"),
- viewOn,
- BSON_ARRAY(BSON("$lookup" << BSON("from"
- << "fcoll"
- << "as"
- << "as"
- << "pipeline"
- << BSONArray()))),
- emptyCollation));
+ ASSERT_OK(createView(operationContext(),
+ NamespaceString("db.view"),
+ viewOn,
+ BSON_ARRAY(BSON("$lookup" << BSON("from"
+ << "fcoll"
+ << "as"
+ << "as"
+ << "pipeline"
+ << BSONArray()))),
+ emptyCollation));
}
TEST_F(ViewCatalogFixture, CreateViewWithPipelineFailsOnInvalidStageName) {
@@ -225,9 +266,8 @@ TEST_F(ViewCatalogFixture, CreateViewWithPipelineFailsOnInvalidStageName) {
const NamespaceString viewOn("db.coll");
auto invalidPipeline = BSON_ARRAY(BSON("INVALID_STAGE_NAME" << 1));
- ASSERT_THROWS(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, invalidPipeline, emptyCollation),
- AssertionException);
+ ASSERT_THROWS(createView(operationContext(), viewName, viewOn, invalidPipeline, emptyCollation),
+ AssertionException);
}
TEST_F(ReplViewCatalogFixture, CreateViewWithPipelineFailsOnIneligibleStage) {
@@ -238,7 +278,7 @@ TEST_F(ReplViewCatalogFixture, CreateViewWithPipelineFailsOnIneligibleStage) {
auto invalidPipeline = BSON_ARRAY(BSON("$changeStream" << BSONObj()));
ASSERT_THROWS_CODE(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, invalidPipeline, emptyCollation),
+ createView(operationContext(), viewName, viewOn, invalidPipeline, emptyCollation),
AssertionException,
ErrorCodes::OptionNotSupportedOnView);
}
@@ -252,7 +292,7 @@ TEST_F(ReplViewCatalogFixture, CreateViewWithPipelineFailsOnIneligibleStagePersi
<< "someOtherCollection"));
ASSERT_THROWS_CODE(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, invalidPipeline, emptyCollation),
+ createView(operationContext(), viewName, viewOn, invalidPipeline, emptyCollation),
AssertionException,
ErrorCodes::OptionNotSupportedOnView);
@@ -260,7 +300,7 @@ TEST_F(ReplViewCatalogFixture, CreateViewWithPipelineFailsOnIneligibleStagePersi
<< "someOtherCollection"));
ASSERT_THROWS_CODE(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, invalidPipeline, emptyCollation),
+ createView(operationContext(), viewName, viewOn, invalidPipeline, emptyCollation),
AssertionException,
ErrorCodes::OptionNotSupportedOnView);
}
@@ -269,8 +309,7 @@ TEST_F(ViewCatalogFixture, CreateViewOnInvalidCollectionName) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.$coll");
- ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_NOT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
TEST_F(ViewCatalogFixture, ExceedMaxViewDepthInOrder) {
@@ -281,15 +320,13 @@ TEST_F(ViewCatalogFixture, ExceedMaxViewDepthInOrder) {
const NamespaceString viewName(str::stream() << ns << i);
const NamespaceString viewOn(str::stream() << ns << (i + 1));
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
const NamespaceString viewName(str::stream() << ns << i);
const NamespaceString viewOn(str::stream() << ns << (i + 1));
- ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_NOT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
TEST_F(ViewCatalogFixture, ExceedMaxViewDepthByJoining) {
@@ -301,23 +338,20 @@ TEST_F(ViewCatalogFixture, ExceedMaxViewDepthByJoining) {
const NamespaceString viewName(str::stream() << ns << i);
const NamespaceString viewOn(str::stream() << ns << (i + 1));
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
for (i = 1; i < size + 1; i++) {
const NamespaceString viewName(str::stream() << ns << (size + i));
const NamespaceString viewOn(str::stream() << ns << (size + i + 1));
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
const NamespaceString viewName(str::stream() << ns << size);
const NamespaceString viewOn(str::stream() << ns << (size + 1));
- ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_NOT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
TEST_F(ViewCatalogFixture, CreateViewCycles) {
@@ -326,7 +360,7 @@ TEST_F(ViewCatalogFixture, CreateViewCycles) {
const NamespaceString viewOn("db.view1");
ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
{
@@ -334,10 +368,9 @@ TEST_F(ViewCatalogFixture, CreateViewCycles) {
const NamespaceString view2("db.view2");
const NamespaceString view3("db.view3");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view1, view2, emptyPipeline, emptyCollation));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view2, view3, emptyPipeline, emptyCollation));
- ASSERT_NOT_OK(
- viewCatalog.createView(opCtx.get(), view3, view1, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), view1, view2, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), view2, view3, emptyPipeline, emptyCollation));
+ ASSERT_NOT_OK(createView(operationContext(), view3, view1, emptyPipeline, emptyCollation));
}
}
@@ -357,7 +390,7 @@ TEST_F(ViewCatalogFixture, CanSuccessfullyCreateViewWhosePipelineIsExactlyAtMaxS
const NamespaceString viewOn("db.coll");
const BSONObj collation;
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, builder.arr(), collation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, builder.arr(), collation));
}
TEST_F(ViewCatalogFixture, CannotCreateViewWhosePipelineExceedsMaxSizeInBytes) {
@@ -374,7 +407,7 @@ TEST_F(ViewCatalogFixture, CannotCreateViewWhosePipelineExceedsMaxSizeInBytes) {
const NamespaceString viewOn("db.coll");
const BSONObj collation;
- ASSERT_NOT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, builder.arr(), collation));
+ ASSERT_NOT_OK(createView(operationContext(), viewName, viewOn, builder.arr(), collation));
}
TEST_F(ViewCatalogFixture, CannotCreateViewIfItsFullyResolvedPipelineWouldExceedMaxSizeInBytes) {
@@ -394,34 +427,34 @@ TEST_F(ViewCatalogFixture, CannotCreateViewIfItsFullyResolvedPipelineWouldExceed
const BSONObj collation1;
const BSONObj collation2;
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view1, viewOn, builder1.arr(), collation1));
- ASSERT_NOT_OK(viewCatalog.createView(opCtx.get(), view2, view1, builder2.arr(), collation2));
+ ASSERT_OK(createView(operationContext(), view1, viewOn, builder1.arr(), collation1));
+ ASSERT_NOT_OK(createView(operationContext(), view2, view1, builder2.arr(), collation2));
}
TEST_F(ViewCatalogFixture, DropMissingView) {
NamespaceString viewName("db.view");
- ASSERT_NOT_OK(viewCatalog.dropView(opCtx.get(), viewName));
+ ASSERT_NOT_OK(dropView(operationContext(), viewName));
}
TEST_F(ViewCatalogFixture, ModifyMissingView) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
- ASSERT_NOT_OK(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, emptyPipeline));
+ ASSERT_NOT_OK(modifyView(operationContext(), viewName, viewOn, emptyPipeline));
}
TEST_F(ViewCatalogFixture, ModifyViewOnDifferentDatabase) {
const NamespaceString viewName("db1.view");
const NamespaceString viewOn("db2.coll");
- ASSERT_NOT_OK(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, emptyPipeline));
+ ASSERT_NOT_OK(modifyView(operationContext(), viewName, viewOn, emptyPipeline));
}
TEST_F(ViewCatalogFixture, ModifyViewOnInvalidCollectionName) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.$coll");
- ASSERT_NOT_OK(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, emptyPipeline));
+ ASSERT_NOT_OK(modifyView(operationContext(), viewName, viewOn, emptyPipeline));
}
TEST_F(ReplViewCatalogFixture, ModifyViewWithPipelineFailsOnIneligibleStage) {
@@ -432,35 +465,35 @@ TEST_F(ReplViewCatalogFixture, ModifyViewWithPipelineFailsOnIneligibleStage) {
auto invalidPipeline = BSON_ARRAY(BSON("$changeStream" << BSONObj()));
// Create the initial, valid view.
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, validPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, validPipeline, emptyCollation));
// Now attempt to replace it with a pipeline containing $changeStream.
- ASSERT_THROWS_CODE(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, invalidPipeline),
+ ASSERT_THROWS_CODE(modifyView(operationContext(), viewName, viewOn, invalidPipeline),
AssertionException,
ErrorCodes::OptionNotSupportedOnView);
}
TEST_F(ViewCatalogFixture, LookupMissingView) {
- ASSERT(!viewCatalog.lookup(opCtx.get(), "db.view"_sd));
+ ASSERT(!lookup(operationContext(), "db.view"_sd));
}
TEST_F(ViewCatalogFixture, LookupExistingView) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
- ASSERT(viewCatalog.lookup(opCtx.get(), "db.view"_sd));
+ ASSERT(lookup(operationContext(), "db.view"_sd));
}
TEST_F(ViewCatalogFixture, LookupRIDExistingView) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
auto resourceID = ResourceId(RESOURCE_COLLECTION, "db.view"_sd);
- auto& collectionCatalog = CollectionCatalog::get(opCtx.get());
+ auto& collectionCatalog = CollectionCatalog::get(operationContext());
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == "db.view");
}
@@ -468,12 +501,19 @@ TEST_F(ViewCatalogFixture, LookupRIDExistingViewRollback) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
{
- WriteUnitOfWork wunit(opCtx.get());
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ Lock::DBLock dbLock(operationContext(), viewName.db(), MODE_X);
+ Lock::CollectionLock collLock(operationContext(), viewName, MODE_IX);
+ Lock::CollectionLock sysCollLock(
+ operationContext(),
+ NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName),
+ MODE_X);
+
+ WriteUnitOfWork wunit(operationContext());
+ ASSERT_OK(getViewCatalog()->createView(
+ operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
}
auto resourceID = ResourceId(RESOURCE_COLLECTION, "db.view"_sd);
- auto& collectionCatalog = CollectionCatalog::get(opCtx.get());
+ auto& collectionCatalog = CollectionCatalog::get(operationContext());
ASSERT(!collectionCatalog.lookupResourceName(resourceID));
}
@@ -481,11 +521,11 @@ TEST_F(ViewCatalogFixture, LookupRIDAfterDrop) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
- ASSERT_OK(viewCatalog.dropView(opCtx.get(), viewName));
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(dropView(operationContext(), viewName));
auto resourceID = ResourceId(RESOURCE_COLLECTION, "db.view"_sd);
- auto& collectionCatalog = CollectionCatalog::get(opCtx.get());
+ auto& collectionCatalog = CollectionCatalog::get(operationContext());
ASSERT(!collectionCatalog.lookupResourceName(resourceID));
}
@@ -494,18 +534,24 @@ TEST_F(ViewCatalogFixture, LookupRIDAfterDropRollback) {
const NamespaceString viewOn("db.coll");
auto resourceID = ResourceId(RESOURCE_COLLECTION, "db.view"_sd);
- auto& collectionCatalog = CollectionCatalog::get(opCtx.get());
+ auto& collectionCatalog = CollectionCatalog::get(operationContext());
{
- WriteUnitOfWork wunit(opCtx.get());
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ WriteUnitOfWork wunit(operationContext());
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == viewName.ns());
wunit.commit();
}
{
- WriteUnitOfWork wunit(opCtx.get());
- ASSERT_OK(viewCatalog.dropView(opCtx.get(), viewName));
+ Lock::DBLock dbLock(operationContext(), viewName.db(), MODE_X);
+ Lock::CollectionLock collLock(operationContext(), viewName, MODE_IX);
+ Lock::CollectionLock sysCollLock(
+ operationContext(),
+ NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName),
+ MODE_X);
+
+ WriteUnitOfWork wunit(operationContext());
+ ASSERT_OK(getViewCatalog()->dropView(operationContext(), viewName));
}
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == viewName.ns());
@@ -516,9 +562,9 @@ TEST_F(ViewCatalogFixture, LookupRIDAfterModify) {
const NamespaceString viewOn("db.coll");
auto resourceID = ResourceId(RESOURCE_COLLECTION, "db.view"_sd);
- auto& collectionCatalog = CollectionCatalog::get(opCtx.get());
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
- ASSERT_OK(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, emptyPipeline));
+ auto& collectionCatalog = CollectionCatalog::get(operationContext());
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(modifyView(operationContext(), viewName, viewOn, emptyPipeline));
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == viewName.ns());
}
@@ -527,17 +573,24 @@ TEST_F(ViewCatalogFixture, LookupRIDAfterModifyRollback) {
const NamespaceString viewOn("db.coll");
auto resourceID = ResourceId(RESOURCE_COLLECTION, "db.view"_sd);
- auto& collectionCatalog = CollectionCatalog::get(opCtx.get());
+ auto& collectionCatalog = CollectionCatalog::get(operationContext());
{
- WriteUnitOfWork wunit(opCtx.get());
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
+ WriteUnitOfWork wunit(operationContext());
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == viewName.ns());
wunit.commit();
}
{
- WriteUnitOfWork wunit(opCtx.get());
- ASSERT_OK(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, emptyPipeline));
+ Lock::DBLock dbLock(operationContext(), viewName.db(), MODE_X);
+ Lock::CollectionLock collLock(operationContext(), viewName, MODE_IX);
+ Lock::CollectionLock sysCollLock(
+ operationContext(),
+ NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName),
+ MODE_X);
+
+ WriteUnitOfWork wunit(operationContext());
+ ASSERT_OK(
+ getViewCatalog()->modifyView(operationContext(), viewName, viewOn, emptyPipeline));
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == viewName.ns());
}
ASSERT(collectionCatalog.lookupResourceName(resourceID).get() == viewName.ns());
@@ -547,32 +600,10 @@ TEST_F(ViewCatalogFixture, CreateViewThenDropAndLookup) {
const NamespaceString viewName("db.view");
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
- ASSERT_OK(viewCatalog.dropView(opCtx.get(), viewName));
-
- ASSERT(!viewCatalog.lookup(opCtx.get(), "db.view"_sd));
-}
-
-TEST_F(ViewCatalogFixture, ModifyTenTimes) {
- const char* ns = "db.view";
- int i;
-
- for (i = 0; i < 5; i++) {
- const NamespaceString viewName(str::stream() << ns << i);
- const NamespaceString viewOn(str::stream() << ns << (i + 1));
-
- ASSERT_OK(
- viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
- }
-
- for (i = 0; i < 5; i++) {
- const NamespaceString viewName(str::stream() << ns << i);
- const NamespaceString viewOn(str::stream() << ns << (i + 1));
-
- ASSERT_OK(viewCatalog.modifyView(opCtx.get(), viewName, viewOn, emptyPipeline));
- }
+ ASSERT_OK(createView(operationContext(), viewName, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(dropView(operationContext(), viewName));
- ASSERT_EQ(10, durableViewCatalog->getUpsertCount());
+ ASSERT(!lookup(operationContext(), "db.view"_sd));
}
TEST_F(ViewCatalogFixture, Iterate) {
@@ -581,13 +612,14 @@ TEST_F(ViewCatalogFixture, Iterate) {
const NamespaceString view3("db.view3");
const NamespaceString viewOn("db.coll");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view1, viewOn, emptyPipeline, emptyCollation));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view2, viewOn, emptyPipeline, emptyCollation));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view3, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), view1, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), view2, viewOn, emptyPipeline, emptyCollation));
+ ASSERT_OK(createView(operationContext(), view3, viewOn, emptyPipeline, emptyCollation));
std::set<std::string> viewNames = {"db.view1", "db.view2", "db.view3"};
- viewCatalog.iterate(opCtx.get(), [&viewNames](const ViewDefinition& view) {
+ Lock::DBLock dbLock(operationContext(), "db", MODE_IX);
+ getViewCatalog()->iterate(operationContext(), [&viewNames](const ViewDefinition& view) {
std::string name = view.name().toString();
ASSERT(viewNames.end() != viewNames.find(name));
viewNames.erase(name);
@@ -609,11 +641,12 @@ TEST_F(ViewCatalogFixture, ResolveViewCorrectPipeline) {
pipeline2 << BSON("$match" << BSON("foo" << 2));
pipeline3 << BSON("$match" << BSON("foo" << 3));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view1, viewOn, pipeline1.arr(), emptyCollation));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view2, view1, pipeline2.arr(), emptyCollation));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view3, view2, pipeline3.arr(), emptyCollation));
+ ASSERT_OK(createView(operationContext(), view1, viewOn, pipeline1.arr(), emptyCollation));
+ ASSERT_OK(createView(operationContext(), view2, view1, pipeline2.arr(), emptyCollation));
+ ASSERT_OK(createView(operationContext(), view3, view2, pipeline3.arr(), emptyCollation));
- auto resolvedView = viewCatalog.resolveView(opCtx.get(), view3);
+ Lock::DBLock dbLock(operationContext(), "db", MODE_IX);
+ auto resolvedView = getViewCatalog()->resolveView(operationContext(), view3);
ASSERT(resolvedView.isOK());
std::vector<BSONObj> expected = {BSON("$match" << BSON("foo" << 1)),
@@ -632,7 +665,9 @@ TEST_F(ViewCatalogFixture, ResolveViewCorrectPipeline) {
TEST_F(ViewCatalogFixture, ResolveViewOnCollectionNamespace) {
const NamespaceString collectionNamespace("db.coll");
- auto resolvedView = uassertStatusOK(viewCatalog.resolveView(opCtx.get(), collectionNamespace));
+ Lock::DBLock dbLock(operationContext(), "db", MODE_IS);
+ auto resolvedView =
+ uassertStatusOK(getViewCatalog()->resolveView(operationContext(), collectionNamespace));
ASSERT_EQ(resolvedView.getNamespace(), collectionNamespace);
ASSERT_EQ(resolvedView.getPipeline().size(), 0U);
@@ -649,12 +684,13 @@ TEST_F(ViewCatalogFixture, ResolveViewCorrectlyExtractsDefaultCollation) {
pipeline2 << BSON("$match" << BSON("foo" << 2));
BSONObj collation = BSON("locale"
- << "mock_reverse_string");
+ << "en_US");
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view1, viewOn, pipeline1.arr(), collation));
- ASSERT_OK(viewCatalog.createView(opCtx.get(), view2, view1, pipeline2.arr(), collation));
+ ASSERT_OK(createView(operationContext(), view1, viewOn, pipeline1.arr(), collation));
+ ASSERT_OK(createView(operationContext(), view2, view1, pipeline2.arr(), collation));
- auto resolvedView = viewCatalog.resolveView(opCtx.get(), view2);
+ Lock::DBLock dbLock(operationContext(), "db", MODE_IS);
+ auto resolvedView = getViewCatalog()->resolveView(operationContext(), view2);
ASSERT(resolvedView.isOK());
ASSERT_EQ(resolvedView.getValue().getNamespace(), viewOn);
@@ -667,26 +703,12 @@ TEST_F(ViewCatalogFixture, ResolveViewCorrectlyExtractsDefaultCollation) {
ASSERT(SimpleBSONObjComparator::kInstance.evaluate(expected[i] == result[i]));
}
- auto expectedCollation =
- CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collation);
+ auto expectedCollation = CollatorFactoryInterface::get(operationContext()->getServiceContext())
+ ->makeFromBSON(collation);
ASSERT_OK(expectedCollation.getStatus());
ASSERT_BSONOBJ_EQ(resolvedView.getValue().getDefaultCollation(),
expectedCollation.getValue()->getSpec().toBSON());
}
-TEST_F(ViewCatalogFixture, InvalidateThenReload) {
- const NamespaceString viewName("db.view");
- const NamespaceString viewOn("db.coll");
-
- ASSERT_OK(viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation));
- ASSERT_EQ(1, durableViewCatalog->getIterateCount());
-
- ASSERT(viewCatalog.lookup(opCtx.get(), "db.view"_sd));
- ASSERT_EQ(1, durableViewCatalog->getIterateCount());
-
- viewCatalog.invalidate();
- ASSERT_OK(viewCatalog.reloadIfNeeded(opCtx.get()));
- ASSERT_EQ(2, durableViewCatalog->getIterateCount());
-}
} // namespace
} // namespace mongo