summaryrefslogtreecommitdiff
path: root/src/mongo/db/views
diff options
context:
space:
mode:
authorKyle Suarez <kyle.suarez@mongodb.com>2018-07-05 22:56:22 -0400
committerKyle Suarez <kyle.suarez@mongodb.com>2018-07-10 05:21:43 -0400
commite86d684515abe1c4dbf79dbb71741b5db0317039 (patch)
tree88bd87e9218d47b06ad2e220ac8f0475e904b814 /src/mongo/db/views
parenta750bf210f70dd6e15cd65a15d50aeb8cd75fa3f (diff)
downloadmongo-e86d684515abe1c4dbf79dbb71741b5db0317039.tar.gz
SERVER-35929 restart view resolution if catalog is invalidated
Diffstat (limited to 'src/mongo/db/views')
-rw-r--r--src/mongo/db/views/view_catalog.cpp103
1 files changed, 70 insertions, 33 deletions
diff --git a/src/mongo/db/views/view_catalog.cpp b/src/mongo/db/views/view_catalog.cpp
index 107249c3d3d..eb1e6dc6bd9 100644
--- a/src/mongo/db/views/view_catalog.cpp
+++ b/src/mongo/db/views/view_catalog.cpp
@@ -53,10 +53,13 @@
#include "mongo/db/views/resolved_view.h"
#include "mongo/db/views/view.h"
#include "mongo/db/views/view_graph.h"
+#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
namespace mongo {
namespace {
+MONGO_FAIL_POINT_DEFINE(hangDuringViewResolution);
+
StatusWith<std::unique_ptr<CollatorInterface>> parseCollator(OperationContext* opCtx,
BSONObj collationSpec) {
// If 'collationSpec' is empty, return the null collator, which represents the "simple"
@@ -428,45 +431,79 @@ std::shared_ptr<ViewDefinition> ViewCatalog::lookup(OperationContext* opCtx, Str
StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx,
const NamespaceString& nss) {
- stdx::lock_guard<stdx::mutex> lk(_mutex);
- const NamespaceString* resolvedNss = &nss;
- std::vector<BSONObj> resolvedPipeline;
- BSONObj collation;
-
- for (int i = 0; i < ViewGraph::kMaxViewDepth; i++) {
- auto view = _lookup_inlock(opCtx, resolvedNss->ns());
- if (!view) {
- // Return error status if pipeline is too large.
- int pipelineSize = 0;
- for (auto obj : resolvedPipeline) {
- pipelineSize += obj.objsize();
+ stdx::unique_lock<stdx::mutex> lock(_mutex);
+
+ // Keep looping until the resolution completes. If the catalog is invalidated during the
+ // resolution, we start over from the beginning.
+ while (true) {
+ // Points to the name of the most resolved namespace.
+ const NamespaceString* resolvedNss = &nss;
+
+ // Holds the combination of all the resolved views.
+ std::vector<BSONObj> resolvedPipeline;
+
+ // If the catalog has not been tampered with, all views seen during the resolution will have
+ // the same collation. As an optimization, we fill out the collation spec only once.
+ boost::optional<BSONObj> collation;
+
+ // The last seen view definition, which owns the NamespaceString pointed to by
+ // 'resolvedNss'.
+ std::shared_ptr<ViewDefinition> lastViewDefinition;
+
+ int depth = 0;
+ for (; depth < ViewGraph::kMaxViewDepth; depth++) {
+ while (MONGO_FAIL_POINT(hangDuringViewResolution)) {
+ log() << "Yielding mutex and hanging due to 'hangDuringViewResolution' failpoint";
+ lock.unlock();
+ sleepmillis(1000);
+ lock.lock();
}
- if (pipelineSize > ViewGraph::kMaxViewPipelineSizeBytes) {
- return {ErrorCodes::ViewPipelineMaxSizeExceeded,
- str::stream() << "View pipeline exceeds maximum size; maximum size is "
- << ViewGraph::kMaxViewPipelineSizeBytes};
+
+ // If the catalog has been invalidated, bail and restart.
+ if (!_valid.load()) {
+ uassertStatusOK(_reloadIfNeeded_inlock(opCtx));
+ break;
}
- return StatusWith<ResolvedView>(
- {*resolvedNss, std::move(resolvedPipeline), std::move(collation)});
- }
- resolvedNss = &(view->viewOn());
- collation = view->defaultCollator() ? view->defaultCollator()->getSpec().toBSON()
- : CollationSpec::kSimpleSpec;
+ auto view = _lookup_inlock(opCtx, resolvedNss->ns());
+ if (!view) {
+ // Return error status if pipeline is too large.
+ int pipelineSize = 0;
+ for (auto obj : resolvedPipeline) {
+ pipelineSize += obj.objsize();
+ }
+ if (pipelineSize > ViewGraph::kMaxViewPipelineSizeBytes) {
+ return {ErrorCodes::ViewPipelineMaxSizeExceeded,
+ str::stream() << "View pipeline exceeds maximum size; maximum size is "
+ << ViewGraph::kMaxViewPipelineSizeBytes};
+ }
+ return StatusWith<ResolvedView>(
+ {*resolvedNss, std::move(resolvedPipeline), std::move(collation.get())});
+ }
- // Prepend the underlying view's pipeline to the current working pipeline.
- const std::vector<BSONObj>& toPrepend = view->pipeline();
- resolvedPipeline.insert(resolvedPipeline.begin(), toPrepend.begin(), toPrepend.end());
+ resolvedNss = &view->viewOn();
+ if (!collation) {
+ collation = view->defaultCollator() ? view->defaultCollator()->getSpec().toBSON()
+ : CollationSpec::kSimpleSpec;
+ }
- // If the first stage is a $collStats, then we return early with the viewOn namespace.
- if (toPrepend.size() > 0 && !toPrepend[0]["$collStats"].eoo()) {
- return StatusWith<ResolvedView>(
- {*resolvedNss, std::move(resolvedPipeline), std::move(collation)});
+ // Prepend the underlying view's pipeline to the current working pipeline.
+ const std::vector<BSONObj>& toPrepend = view->pipeline();
+ resolvedPipeline.insert(resolvedPipeline.begin(), toPrepend.begin(), toPrepend.end());
+
+ // If the first stage is a $collStats, then we return early with the viewOn namespace.
+ if (toPrepend.size() > 0 && !toPrepend[0]["$collStats"].eoo()) {
+ return StatusWith<ResolvedView>(
+ {*resolvedNss, std::move(resolvedPipeline), std::move(collation.get())});
+ }
}
- }
- return {ErrorCodes::ViewDepthLimitExceeded,
- str::stream() << "View depth too deep or view cycle detected; maximum depth is "
- << ViewGraph::kMaxViewDepth};
+ if (depth >= ViewGraph::kMaxViewDepth) {
+ return {ErrorCodes::ViewDepthLimitExceeded,
+ str::stream() << "View depth too deep or view cycle detected; maximum depth is "
+ << ViewGraph::kMaxViewDepth};
+ }
+ };
+ MONGO_UNREACHABLE;
}
} // namespace mongo