Status PlanCache::feedback(const CanonicalQuery& cq, PlanCacheEntryFeedback* feedback) { if (NULL == feedback) { return Status(ErrorCodes::BadValue, "feedback is NULL"); } std::auto_ptr<PlanCacheEntryFeedback> autoFeedback(feedback); const PlanCacheKey& ck = cq.getPlanCacheKey(); boost::lock_guard<boost::mutex> cacheLock(_cacheMutex); PlanCacheEntry* entry; Status cacheStatus = _cache.get(ck, &entry); if (!cacheStatus.isOK()) { return cacheStatus; } invariant(entry); if (entry->feedback.size() >= size_t(internalQueryCacheFeedbacksStored)) { // If we have enough feedback, then use it to determine whether // we should get rid of the cached solution. if (hasCachedPlanPerformanceDegraded(entry, autoFeedback.get())) { LOG(1) << _ns << ": removing plan cache entry " << entry->toString() << " - detected degradation in performance of cached solution."; _cache.remove(ck); } } else { // We don't have enough feedback yet---just store it and move on. entry->feedback.push_back(autoFeedback.release()); } return Status::OK(); }
Status PlanCache::add(const CanonicalQuery& query, const std::vector<QuerySolution*>& solns, PlanRankingDecision* why) { invariant(why); if (solns.empty()) { return Status(ErrorCodes::BadValue, "no solutions provided"); } if (why->stats.size() != solns.size()) { return Status(ErrorCodes::BadValue, "number of stats in decision must match solutions"); } if (why->scores.size() != solns.size()) { return Status(ErrorCodes::BadValue, "number of scores in decision must match solutions"); } if (why->candidateOrder.size() != solns.size()) { return Status(ErrorCodes::BadValue, "candidate ordering entries in decision must match solutions"); } PlanCacheEntry* entry = new PlanCacheEntry(solns, why); const LiteParsedQuery& pq = query.getParsed(); entry->query = pq.getFilter().getOwned(); entry->sort = pq.getSort().getOwned(); entry->projection = pq.getProj().getOwned(); // If the winning solution uses a blocking stage, then try and // find a fallback solution that has no blocking stage. if (solns[0]->hasBlockingStage) { for (size_t i = 1; i < solns.size(); ++i) { if (!solns[i]->hasBlockingStage) { entry->backupSoln.reset(i); break; } } } boost::lock_guard<boost::mutex> cacheLock(_cacheMutex); std::auto_ptr<PlanCacheEntry> evictedEntry = _cache.add(query.getPlanCacheKey(), entry); if (NULL != evictedEntry.get()) { LOG(1) << _ns << ": plan cache maximum size exceeded - " << "removed least recently used entry " << evictedEntry->toString(); } return Status::OK(); }
void QuerySettings::removeAllowedIndices(const CanonicalQuery& canonicalQuery) { const PlanCacheKey& key = canonicalQuery.getPlanCacheKey(); boost::lock_guard<boost::mutex> cacheLock(_mutex); AllowedIndexEntryMap::iterator i = _allowedIndexEntryMap.find(key); // Nothing to do if key does not exist in query settings. if (i == _allowedIndexEntryMap.end()) { return; } // Free up resources and delete entry. AllowedIndexEntry* entry = i->second; _allowedIndexEntryMap.erase(i); delete entry; }
Status PlanCache::getEntry(const CanonicalQuery& query, PlanCacheEntry** entryOut) const { const PlanCacheKey& key = query.getPlanCacheKey(); verify(entryOut); boost::lock_guard<boost::mutex> cacheLock(_cacheMutex); PlanCacheEntry* entry; Status cacheStatus = _cache.get(key, &entry); if (!cacheStatus.isOK()) { return cacheStatus; } invariant(entry); *entryOut = entry->clone(); return Status::OK(); }
Status PlanCache::get(const CanonicalQuery& query, CachedSolution** crOut) const { const PlanCacheKey& key = query.getPlanCacheKey(); verify(crOut); boost::lock_guard<boost::mutex> cacheLock(_cacheMutex); PlanCacheEntry* entry; Status cacheStatus = _cache.get(key, &entry); if (!cacheStatus.isOK()) { return cacheStatus; } invariant(entry); *crOut = new CachedSolution(key, *entry); return Status::OK(); }
void QuerySettings::setAllowedIndices(const CanonicalQuery& canonicalQuery, const std::vector<BSONObj>& indexes) { const LiteParsedQuery& lpq = canonicalQuery.getParsed(); const BSONObj& query = lpq.getFilter(); const BSONObj& sort = lpq.getSort(); const BSONObj& projection = lpq.getProj(); AllowedIndexEntry* entry = new AllowedIndexEntry(query, sort, projection, indexes); const PlanCacheKey& key = canonicalQuery.getPlanCacheKey(); boost::lock_guard<boost::mutex> cacheLock(_mutex); AllowedIndexEntryMap::iterator i = _allowedIndexEntryMap.find(key); // Replace existing entry. if (i != _allowedIndexEntryMap.end()) { AllowedIndexEntry* entry = i->second; delete entry; } _allowedIndexEntryMap[key] = entry; }
bool QuerySettings::getAllowedIndices(const CanonicalQuery& query, AllowedIndices** allowedIndicesOut) const { invariant(allowedIndicesOut); const PlanCacheKey& key = query.getPlanCacheKey(); boost::lock_guard<boost::mutex> cacheLock(_mutex); AllowedIndexEntryMap::const_iterator cacheIter = _allowedIndexEntryMap.find(key); // Nothing to do if key does not exist in query settings. if (cacheIter == _allowedIndexEntryMap.end()) { *allowedIndicesOut = NULL; return false; } AllowedIndexEntry* entry = cacheIter->second; // Create a AllowedIndices from entry. *allowedIndicesOut = new AllowedIndices(entry->indexKeyPatterns); return true; }
bool PlanCache::contains(const CanonicalQuery& cq) const { boost::lock_guard<boost::mutex> cacheLock(_cacheMutex); return _cache.hasKey(cq.getPlanCacheKey()); }
Status PlanCache::remove(const CanonicalQuery& canonicalQuery) { boost::lock_guard<boost::mutex> cacheLock(_cacheMutex); return _cache.remove(canonicalQuery.getPlanCacheKey()); }