summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShin Yee Tan <shinyee.tan@mongodb.com>2022-04-20 17:50:36 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-01-25 16:45:20 +0000
commitc58f4997c05d987a90a77c7870c0e2d742dda86a (patch)
treec5e248d2695cb2df8185f7b34795c6ac6c1c2843
parent6659be6a462ee241f6b0a80a27ae9440bebf9216 (diff)
downloadmongo-c58f4997c05d987a90a77c7870c0e2d742dda86a.tar.gz
SERVER-53870 Improve view creation performance over time by avoiding reloading views from disk
(cherry picked from commit be752f7877f795faa42432be79039faf2b968660)
-rw-r--r--src/mongo/db/catalog/collection_catalog.cpp67
-rw-r--r--src/mongo/db/catalog/collection_catalog.h19
-rw-r--r--src/mongo/db/catalog/views_for_database.cpp97
-rw-r--r--src/mongo/db/catalog/views_for_database.h15
-rw-r--r--src/mongo/db/op_observer_impl.cpp10
-rw-r--r--src/mongo/db/views/SConscript1
-rw-r--r--src/mongo/db/views/durable_view_catalog.cpp120
-rw-r--r--src/mongo/db/views/durable_view_catalog.h8
-rw-r--r--src/mongo/db/views/view_graph.cpp5
-rw-r--r--src/mongo/db/views/view_graph.h17
10 files changed, 242 insertions, 117 deletions
diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp
index 25e79db00b1..5888c6211b9 100644
--- a/src/mongo/db/catalog/collection_catalog.cpp
+++ b/src/mongo/db/catalog/collection_catalog.cpp
@@ -469,13 +469,13 @@ void CollectionCatalog::write(OperationContext* opCtx,
write(opCtx->getServiceContext(), std::move(job));
}
-Status CollectionCatalog::createView(
- OperationContext* opCtx,
- const NamespaceString& viewName,
- const NamespaceString& viewOn,
- const BSONArray& pipeline,
- const BSONObj& collation,
- const ViewsForDatabase::PipelineValidatorFn& pipelineValidator) const {
+Status CollectionCatalog::createView(OperationContext* opCtx,
+ const NamespaceString& viewName,
+ const NamespaceString& viewOn,
+ const BSONArray& pipeline,
+ const BSONObj& collation,
+ const ViewsForDatabase::PipelineValidatorFn& pipelineValidator,
+ const bool updateDurableViewCatalog) const {
invariant(opCtx->lockState()->isCollectionLockedForMode(viewName, MODE_IX));
invariant(opCtx->lockState()->isCollectionLockedForMode(
NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName), MODE_X));
@@ -483,6 +483,11 @@ Status CollectionCatalog::createView(
invariant(_viewsForDatabase.contains(viewName.db()));
const ViewsForDatabase& viewsForDb = *_getViewsForDatabase(opCtx, viewName.db());
+ auto& uncommittedCatalogUpdates = UncommittedCatalogUpdates::get(opCtx);
+ if (uncommittedCatalogUpdates.shouldIgnoreExternalViewChanges(viewName.db())) {
+ return Status::OK();
+ }
+
if (viewName.db() != viewOn.db())
return Status(ErrorCodes::BadValue,
"View must be created on a view or collection in the same database");
@@ -508,7 +513,8 @@ Status CollectionCatalog::createView(
pipeline,
pipelineValidator,
std::move(collator.getValue()),
- ViewsForDatabase{viewsForDb});
+ ViewsForDatabase{viewsForDb},
+ ViewUpsertMode::kCreateView);
}
return result;
@@ -550,7 +556,8 @@ Status CollectionCatalog::modifyView(
pipeline,
pipelineValidator,
CollatorInterface::cloneCollator(viewPtr->defaultCollator()),
- ViewsForDatabase{viewsForDb});
+ ViewsForDatabase{viewsForDb},
+ ViewUpsertMode::kUpdateView);
}
return result;
@@ -1404,15 +1411,16 @@ Status CollectionCatalog::_createOrUpdateView(
const BSONArray& pipeline,
const ViewsForDatabase::PipelineValidatorFn& pipelineValidator,
std::unique_ptr<CollatorInterface> collator,
- ViewsForDatabase&& viewsForDb) const {
+ ViewsForDatabase&& viewsForDb,
+ ViewUpsertMode mode) const {
invariant(opCtx->lockState()->isCollectionLockedForMode(viewName, MODE_IX));
invariant(opCtx->lockState()->isCollectionLockedForMode(
NamespaceString(viewName.db(), NamespaceString::kSystemDotViewsCollectionName), MODE_X));
viewsForDb.requireValidCatalog();
- // 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.
+ // Build the BSON definition for this view to be saved in the durable view catalog and/or to
+ // insert in the viewMap. If the collation is empty, omit it from the definition altogether.
BSONObjBuilder viewDefBuilder;
viewDefBuilder.append("_id", viewName.ns());
viewDefBuilder.append("viewOn", viewOn.coll());
@@ -1421,25 +1429,42 @@ Status CollectionCatalog::_createOrUpdateView(
viewDefBuilder.append("collation", collator->getSpec().toBSON());
}
+ BSONObj viewDef = viewDefBuilder.obj();
BSONObj ownedPipeline = pipeline.getOwned();
- auto view = std::make_shared<ViewDefinition>(
+ ViewDefinition view(
viewName.db(), viewName.coll(), viewOn.coll(), ownedPipeline, std::move(collator));
- // Check that the resulting dependency graph is acyclic and within the maximum depth.
- Status graphStatus = viewsForDb.upsertIntoGraph(opCtx, *(view.get()), pipelineValidator);
+ // If the view is already in the durable view catalog, we don't need to validate the graph. If
+ // we need to update the durable view catalog, we need to check that the resulting dependency
+ // graph is acyclic and within the maximum depth.
+ const bool viewGraphNeedsValidation = mode != ViewUpsertMode::kAlreadyDurableView;
+ Status graphStatus =
+ viewsForDb.upsertIntoGraph(opCtx, view, pipelineValidator, viewGraphNeedsValidation);
if (!graphStatus.isOK()) {
return graphStatus;
}
- viewsForDb.durable->upsert(opCtx, viewName, viewDefBuilder.obj());
+ if (mode != ViewUpsertMode::kAlreadyDurableView) {
+ viewsForDb.durable->upsert(opCtx, viewName, viewDef);
+ }
- viewsForDb.viewMap.clear();
viewsForDb.valid = false;
- viewsForDb.viewGraphNeedsRefresh = true;
- viewsForDb.stats = {};
+ auto res = [&] {
+ switch (mode) {
+ case ViewUpsertMode::kCreateView:
+ case ViewUpsertMode::kAlreadyDurableView:
+ return viewsForDb.insert(opCtx, viewDef);
+ case ViewUpsertMode::kUpdateView:
+ viewsForDb.viewMap.clear();
+ viewsForDb.viewGraphNeedsRefresh = true;
+ viewsForDb.stats = {};
+
+ // Reload the view catalog with the changes applied.
+ return viewsForDb.reload(opCtx);
+ }
+ MONGO_UNREACHABLE;
+ }();
- // Reload the view catalog with the changes applied.
- auto res = viewsForDb.reload(opCtx);
if (res.isOK()) {
auto& uncommittedCatalogUpdates = UncommittedCatalogUpdates::get(opCtx);
uncommittedCatalogUpdates.addView(opCtx, viewName);
diff --git a/src/mongo/db/catalog/collection_catalog.h b/src/mongo/db/catalog/collection_catalog.h
index 98239bcb8bd..a4b2891a1db 100644
--- a/src/mongo/db/catalog/collection_catalog.h
+++ b/src/mongo/db/catalog/collection_catalog.h
@@ -154,7 +154,8 @@ public:
const NamespaceString& viewOn,
const BSONArray& pipeline,
const BSONObj& collation,
- const ViewsForDatabase::PipelineValidatorFn& pipelineValidator) const;
+ const ViewsForDatabase::PipelineValidatorFn& pipelineValidator,
+ bool updateDurableViewCatalog = true) const;
/**
* Drop the view named 'viewName'.
@@ -539,6 +540,19 @@ private:
*/
void _replaceViewsForDatabase(StringData dbName, ViewsForDatabase&& views);
+ enum class ViewUpsertMode {
+ // Insert all data for that view into the view map, view graph, and durable view catalog.
+ kCreateView,
+
+ // Insert into the view map and view graph without reinserting the view into the durable
+ // view catalog. Skip view graph validation.
+ kAlreadyDurableView,
+
+ // Reload the view map, insert into the view graph (flagging it as needing refresh), and
+ // update the durable view catalog.
+ kUpdateView,
+ };
+
/**
* Helper to take care of shared functionality for 'createView(...)' and 'modifyView(...)'.
*/
@@ -548,7 +562,8 @@ private:
const BSONArray& pipeline,
const ViewsForDatabase::PipelineValidatorFn& pipelineValidator,
std::unique_ptr<CollatorInterface> collator,
- ViewsForDatabase&& viewsForDb) const;
+ ViewsForDatabase&& viewsForDb,
+ ViewUpsertMode mode) const;
/**
* Returns true if this CollectionCatalog instance is part of an ongoing batched catalog write.
diff --git a/src/mongo/db/catalog/views_for_database.cpp b/src/mongo/db/catalog/views_for_database.cpp
index 776cf5e3266..404297c11a3 100644
--- a/src/mongo/db/catalog/views_for_database.cpp
+++ b/src/mongo/db/catalog/views_for_database.cpp
@@ -61,47 +61,8 @@ std::shared_ptr<const ViewDefinition> ViewsForDatabase::lookup(const NamespaceSt
}
Status ViewsForDatabase::reload(OperationContext* opCtx) {
- auto reloadCallback = [&](const BSONObj& view) -> Status {
- BSONObj collationSpec = view.hasField("collation") ? view["collation"].Obj() : BSONObj();
- auto collator = parseCollator(opCtx, collationSpec);
- if (!collator.isOK()) {
- return collator.getStatus();
- }
-
- NamespaceString viewName(view["_id"].str());
-
- auto pipeline = view["pipeline"].Obj();
- for (auto&& stage : pipeline) {
- if (BSONType::Object != stage.type()) {
- return Status(ErrorCodes::InvalidViewDefinition,
- str::stream() << "View 'pipeline' entries must be objects, but "
- << viewName.toString()
- << " has a pipeline element of type " << stage.type());
- }
- }
-
- auto viewDef = std::make_shared<ViewDefinition>(viewName.db(),
- viewName.coll(),
- view["viewOn"].str(),
- pipeline,
- std::move(collator.getValue()));
-
- if (!viewName.isOnInternalDb() && !viewName.isSystem()) {
- if (viewDef->timeseries()) {
- stats.userTimeseries += 1;
- } else {
- stats.userViews += 1;
- }
- } else {
- stats.internal += 1;
- }
-
- viewMap[viewName.ns()] = std::move(viewDef);
- return Status::OK();
- };
-
try {
- durable->iterate(opCtx, reloadCallback);
+ durable->iterate(opCtx, [&](const BSONObj& view) { return _insert(opCtx, view); });
} catch (const DBException& ex) {
auto status = ex.toStatus();
LOGV2(22547,
@@ -110,9 +71,60 @@ Status ViewsForDatabase::reload(OperationContext* opCtx) {
"error"_attr = status);
return status;
}
+ valid = true;
+ return Status::OK();
+}
+
+Status ViewsForDatabase::insert(OperationContext* opCtx, const BSONObj& view) {
+ auto status = _insert(opCtx, view);
+ if (!status.isOK()) {
+ LOGV2(5387000,
+ "Could not insert view",
+ "db"_attr = durable->getName(),
+ "error"_attr = status);
+ return status;
+ }
valid = true;
+ return Status::OK();
+};
+
+Status ViewsForDatabase::_insert(OperationContext* opCtx, const BSONObj& view) {
+ BSONObj collationSpec = view.hasField("collation") ? view["collation"].Obj() : BSONObj();
+ auto collator = parseCollator(opCtx, collationSpec);
+ if (!collator.isOK()) {
+ return collator.getStatus();
+ }
+
+ NamespaceString viewName(view["_id"].str());
+
+ auto pipeline = view["pipeline"].Obj();
+ for (auto&& stage : pipeline) {
+ if (BSONType::Object != stage.type()) {
+ return Status(ErrorCodes::InvalidViewDefinition,
+ str::stream() << "View 'pipeline' entries must be objects, but "
+ << viewName.toString() << " has a pipeline element of type "
+ << stage.type());
+ }
+ }
+
+ auto viewDef = std::make_shared<ViewDefinition>(viewName.db(),
+ viewName.coll(),
+ view["viewOn"].str(),
+ pipeline,
+ std::move(collator.getValue()));
+
+ if (!viewName.isOnInternalDb() && !viewName.isSystem()) {
+ if (viewDef->timeseries()) {
+ stats.userTimeseries += 1;
+ } else {
+ stats.userViews += 1;
+ }
+ } else {
+ stats.internal += 1;
+ }
+ viewMap[viewName.ns()] = std::move(viewDef);
return Status::OK();
}
@@ -135,7 +147,8 @@ Status ViewsForDatabase::validateCollation(OperationContext* opCtx,
Status ViewsForDatabase::upsertIntoGraph(OperationContext* opCtx,
const ViewDefinition& viewDef,
- const PipelineValidatorFn& validatePipeline) {
+ const PipelineValidatorFn& validatePipeline,
+ const bool needsValidation) {
// Performs the insert into the graph.
auto doInsert = [this, opCtx, &validatePipeline](const ViewDefinition& viewDef,
bool needsValidation) -> Status {
@@ -190,7 +203,7 @@ Status ViewsForDatabase::upsertIntoGraph(OperationContext* opCtx,
// is simply a no-op.
viewGraph.remove(viewDef.name());
- return doInsert(viewDef, true);
+ return doInsert(viewDef, needsValidation);
}
} // namespace mongo
diff --git a/src/mongo/db/catalog/views_for_database.h b/src/mongo/db/catalog/views_for_database.h
index 914adf60df7..ab4329bf9d9 100644
--- a/src/mongo/db/catalog/views_for_database.h
+++ b/src/mongo/db/catalog/views_for_database.h
@@ -93,6 +93,11 @@ public:
Status reload(OperationContext* opCtx);
/**
+ * Inserts the view into the view map.
+ */
+ Status insert(OperationContext* opCtx, const BSONObj& view);
+
+ /**
* Returns Status::OK if each view namespace in 'refs' has the same default collation as
* 'view'. Otherwise, returns ErrorCodes::OptionNotSupportedOnView.
*/
@@ -103,11 +108,17 @@ public:
/**
* Parses the view definition pipeline, attempts to upsert into the view graph, and
* refreshes the graph if necessary. Returns an error status if the resulting graph
- * would be invalid.
+ * would be invalid. needsValidation can be set to false if the view already exists in the
+ * durable view catalog and skips checking that the resulting dependency graph is acyclic and
+ * within the maximum depth.
*/
Status upsertIntoGraph(OperationContext* opCtx,
const ViewDefinition& viewDef,
- const PipelineValidatorFn&);
+ const PipelineValidatorFn&,
+ bool needsValidation);
+
+private:
+ Status _insert(OperationContext* opCtx, const BSONObj& view);
};
} // namespace mongo
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index 39c07f31b4c..f045cff94ff 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -611,7 +611,15 @@ void OpObserverImpl::onInserts(OperationContext* opCtx,
if (nss.coll() == "system.js") {
Scope::storedFuncMod(opCtx);
} else if (nss.coll() == DurableViewCatalog::viewsCollectionName()) {
- DurableViewCatalog::onExternalChange(opCtx, nss);
+ try {
+ for (auto it = first; it != last; it++) {
+ uassertStatusOK(DurableViewCatalog::onExternalInsert(opCtx, it->doc, nss));
+ }
+ } catch (const DBException&) {
+ // If a previous operation left the view catalog in an invalid state, our inserts can
+ // fail even if all the definitions are valid. Reloading may help us reset the state.
+ DurableViewCatalog::onExternalChange(opCtx, nss);
+ }
} else if (nss == NamespaceString::kSessionTransactionsTableNamespace && !lastOpTime.isNull()) {
for (auto it = first; it != last; it++) {
MongoDSessionCatalog::observeDirectWriteToConfigTransactions(opCtx, it->doc);
diff --git a/src/mongo/db/views/SConscript b/src/mongo/db/views/SConscript
index a16bf79890a..94d6b3edd39 100644
--- a/src/mongo/db/views/SConscript
+++ b/src/mongo/db/views/SConscript
@@ -17,6 +17,7 @@ env.Library(
'$BUILD_DIR/mongo/db/audit',
'$BUILD_DIR/mongo/db/catalog/database_holder',
'$BUILD_DIR/mongo/db/multitenancy',
+ '$BUILD_DIR/mongo/db/views/view_catalog_helpers',
],
)
diff --git a/src/mongo/db/views/durable_view_catalog.cpp b/src/mongo/db/views/durable_view_catalog.cpp
index c96b7af6672..9faf0362a31 100644
--- a/src/mongo/db/views/durable_view_catalog.cpp
+++ b/src/mongo/db/views/durable_view_catalog.cpp
@@ -47,6 +47,7 @@
#include "mongo/db/operation_context.h"
#include "mongo/db/storage/record_data.h"
#include "mongo/db/tenant_database_name.h"
+#include "mongo/db/views/view_catalog_helpers.h"
#include "mongo/logv2/log.h"
#include "mongo/stdx/unordered_set.h"
#include "mongo/util/assert_util.h"
@@ -54,6 +55,53 @@
namespace mongo {
+namespace {
+void validateViewDefinitionBSON(OperationContext* opCtx,
+ const BSONObj& viewDefinition,
+ StringData dbName) {
+ bool valid = true;
+
+ for (const BSONElement& e : viewDefinition) {
+ std::string name(e.fieldName());
+ valid &= name == "_id" || name == "viewOn" || name == "pipeline" || name == "collation" ||
+ name == "timeseries";
+ }
+
+ const auto viewName = viewDefinition["_id"].str();
+ const auto viewNameIsValid = NamespaceString::validCollectionComponent(viewName) &&
+ NamespaceString::validDBName(nsToDatabaseSubstring(viewName));
+ valid &= viewNameIsValid;
+
+ // Only perform validation via NamespaceString if the collection name has been determined to
+ // be valid. If not valid then the NamespaceString constructor will uassert.
+ if (viewNameIsValid) {
+ NamespaceString viewNss(viewName);
+ valid &= viewNss.isValid() && viewNss.db() == dbName;
+ }
+
+ valid &= NamespaceString::validCollectionName(viewDefinition["viewOn"].str());
+
+ const bool hasPipeline = viewDefinition.hasField("pipeline");
+ valid &= hasPipeline;
+ if (hasPipeline) {
+ valid &= viewDefinition["pipeline"].type() == mongo::Array;
+ }
+
+ valid &= (!viewDefinition.hasField("collation") ||
+ viewDefinition["collation"].type() == BSONType::Object);
+
+ valid &= !viewDefinition.hasField("timeseries") ||
+ viewDefinition["timeseries"].type() == BSONType::Object;
+
+ uassert(ErrorCodes::InvalidViewDefinition,
+ str::stream() << "found invalid view definition " << viewDefinition["_id"]
+ << " while reading '"
+ << NamespaceString(dbName, NamespaceString::kSystemDotViewsCollectionName)
+ << "'",
+ valid);
+}
+} // namespace
+
// DurableViewCatalog
void DurableViewCatalog::onExternalChange(OperationContext* opCtx, const NamespaceString& name) {
@@ -68,6 +116,33 @@ void DurableViewCatalog::onExternalChange(OperationContext* opCtx, const Namespa
catalog->reloadViews(opCtx, name.db()).ignore();
}
+Status DurableViewCatalog::onExternalInsert(OperationContext* opCtx,
+ const BSONObj& doc,
+ const NamespaceString& name) {
+ try {
+ validateViewDefinitionBSON(opCtx, doc, name.toString());
+ } catch (const DBException& e) {
+ return e.toStatus();
+ }
+
+ auto catalog = CollectionCatalog::get(opCtx);
+ NamespaceString viewName(doc.getStringField("_id"));
+ NamespaceString viewOn(name.db(), doc.getStringField("viewOn"));
+ BSONArray pipeline(doc.getObjectField("pipeline"));
+ BSONObj collation(doc.getObjectField("collation"));
+ // Set updateDurableViewCatalog to false because the view has already been inserted into the
+ // durable view catalog.
+ const bool updateDurableViewCatalog = false;
+
+ return catalog->createView(opCtx,
+ viewName,
+ viewOn,
+ pipeline,
+ collation,
+ view_catalog_helpers::validatePipeline,
+ updateDurableViewCatalog);
+}
+
void DurableViewCatalog::onSystemViewsCollectionDrop(OperationContext* opCtx,
const NamespaceString& name) {
dassert(opCtx->lockState()->isDbLockedForMode(name.db(), MODE_IX));
@@ -127,9 +202,9 @@ void DurableViewCatalogImpl::_iterate(OperationContext* opCtx,
try {
viewDefinition = _validateViewDefinition(opCtx, record->data);
uassertStatusOK(callback(viewDefinition));
- } catch (const ExceptionFor<ErrorCodes::InvalidViewDefinition>& ex) {
+ } catch (const ExceptionFor<ErrorCodes::InvalidViewDefinition>&) {
if (lookupBehavior == ViewCatalogLookupBehavior::kValidateViews) {
- throw ex;
+ throw;
}
}
}
@@ -142,46 +217,9 @@ BSONObj DurableViewCatalogImpl::_validateViewDefinition(OperationContext* opCtx,
// decimal data even if decimal is disabled.
fassert(40224, validateBSON(recordData.data(), recordData.size()));
BSONObj viewDefinition = recordData.toBson();
+ std::string dbName(_db->name().dbName());
- bool valid = true;
-
- for (const BSONElement& e : viewDefinition) {
- std::string name(e.fieldName());
- valid &= name == "_id" || name == "viewOn" || name == "pipeline" || name == "collation" ||
- name == "timeseries";
- }
-
- const auto viewName = viewDefinition["_id"].str();
- const auto viewNameIsValid = NamespaceString::validCollectionComponent(viewName) &&
- NamespaceString::validDBName(nsToDatabaseSubstring(viewName));
- valid &= viewNameIsValid;
-
- // Only perform validation via NamespaceString if the collection name has been determined to
- // be valid. If not valid then the NamespaceString constructor will uassert.
- if (viewNameIsValid) {
- NamespaceString viewNss(viewName);
- valid &= viewNss.isValid() && viewNss.db() == _db->name().dbName();
- }
-
- valid &= NamespaceString::validCollectionName(viewDefinition["viewOn"].str());
-
- const bool hasPipeline = viewDefinition.hasField("pipeline");
- valid &= hasPipeline;
- if (hasPipeline) {
- valid &= viewDefinition["pipeline"].type() == mongo::Array;
- }
-
- valid &= (!viewDefinition.hasField("collation") ||
- viewDefinition["collation"].type() == BSONType::Object);
-
- valid &= !viewDefinition.hasField("timeseries") ||
- viewDefinition["timeseries"].type() == BSONType::Object;
-
- uassert(ErrorCodes::InvalidViewDefinition,
- str::stream() << "found invalid view definition " << viewDefinition["_id"]
- << " while reading '" << _db->getSystemViewsName() << "'",
- valid);
-
+ validateViewDefinitionBSON(opCtx, viewDefinition, dbName);
return viewDefinition;
}
diff --git a/src/mongo/db/views/durable_view_catalog.h b/src/mongo/db/views/durable_view_catalog.h
index 0546636225d..6b938c6f54c 100644
--- a/src/mongo/db/views/durable_view_catalog.h
+++ b/src/mongo/db/views/durable_view_catalog.h
@@ -68,6 +68,14 @@ public:
static void onExternalChange(OperationContext* opCtx, const NamespaceString& name);
/**
+ * Thread-safe method to insert into the in-memory view catalog when there is an insert to
+ * 'system.views'.
+ */
+ static Status onExternalInsert(OperationContext* opCtx,
+ const BSONObj& doc,
+ const NamespaceString& name);
+
+ /**
* Thread-safe method to clear the in-memory state of the view catalog when the 'system.views'
* collection is dropped.
*/
diff --git a/src/mongo/db/views/view_graph.cpp b/src/mongo/db/views/view_graph.cpp
index 4282a0469a4..716dd4e9d32 100644
--- a/src/mongo/db/views/view_graph.cpp
+++ b/src/mongo/db/views/view_graph.cpp
@@ -126,10 +126,9 @@ void ViewGraph::insertWithoutValidating(const ViewDefinition& view,
// pointers for its children.
Node* node = &(_graph[nodeId]);
invariant(node->children.empty());
- invariant(!static_cast<bool>(node->collator));
node->size = pipelineSize;
- node->collator = view.defaultCollator();
+ node->collator = CollatorInterface::cloneCollator(view.defaultCollator());
for (const NamespaceString& childNss : refs) {
uint64_t childId = _getNodeId(childNss);
@@ -165,7 +164,7 @@ void ViewGraph::remove(const NamespaceString& viewNss) {
// This node no longer represents a view, so its children must be cleared and its collator
// unset.
node->children.clear();
- node->collator = boost::none;
+ node->collator = nullptr;
// Only remove node if there are no remaining references to this node.
if (node->parents.size() == 0) {
diff --git a/src/mongo/db/views/view_graph.h b/src/mongo/db/views/view_graph.h
index 2653f1b5aa1..98fefdb91dc 100644
--- a/src/mongo/db/views/view_graph.h
+++ b/src/mongo/db/views/view_graph.h
@@ -105,11 +105,18 @@ private:
// This node represents a view namespace if and only if 'children' is nonempty and 'collator' is
// set.
struct Node {
+ Node() = default;
+ Node(const Node& other)
+ : ns(other.ns), children(other.children), parents(other.parents), size(other.size) {
+ if (other.collator) {
+ collator = CollatorInterface::cloneCollator(other.collator.get());
+ }
+ }
+
/**
* Returns true if this node represents a view.
*/
bool isView() const {
- invariant(children.empty() == !static_cast<bool>(collator));
return !children.empty();
}
@@ -124,10 +131,10 @@ private:
// Represents the views that depend on this namespace.
stdx::unordered_set<uint64_t> parents;
- // When set, this is an unowned pointer to the view's collation, or nullptr if the view has
- // the binary collation. When not set, this namespace is not a view and we don't care about
- // its collator.
- boost::optional<const CollatorInterface*> collator;
+ // When set to nullptr, the view either has the binary collation or this namespace is not a
+ // view and we don't care about its collator. Verify if view with isView. ViewGraph owns the
+ // collator in order to keep pointer alive after insertion.
+ std::unique_ptr<const CollatorInterface> collator;
// The size of this view's "pipeline", in bytes.
int size = 0;