ViewDefinition* ViewCatalog::_lookup_inlock(OperationContext* txn, StringData ns) { uassertStatusOK(_reloadIfNeeded_inlock(txn)); ViewMap::const_iterator it = _viewMap.find(ns); if (it != _viewMap.end()) { return it->second.get(); } return nullptr; }
std::shared_ptr<ViewDefinition> ViewCatalog::_lookup_inlock(OperationContext* txn, StringData ns) { // 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_inlock(txn); // 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 (txn->getClient()->isFromUserConnection()) uassertStatusOK(status); } ViewMap::const_iterator it = _viewMap.find(ns); if (it != _viewMap.end()) { return it->second; } return nullptr; }
Status ViewCatalog::reloadIfNeeded(OperationContext* txn) { stdx::lock_guard<stdx::mutex> lk(_mutex); return _reloadIfNeeded_inlock(txn); }
StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx, const NamespaceString& nss) { 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 the catalog has been invalidated, bail and restart. if (!_valid.load()) { uassertStatusOK(_reloadIfNeeded_inlock(opCtx)); break; } 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())}); } resolvedNss = &view->viewOn(); if (!collation) { collation = view->defaultCollator() ? view->defaultCollator()->getSpec().toBSON() : CollationSpec::kSimpleSpec; } // 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())}); } } if (depth >= ViewGraph::kMaxViewDepth) { return {ErrorCodes::ViewDepthLimitExceeded, str::stream() << "View depth too deep or view cycle detected; maximum depth is " << ViewGraph::kMaxViewDepth}; } }; MONGO_UNREACHABLE; }