diff options
author | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2015-08-27 19:21:53 -0400 |
---|---|---|
committer | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2015-08-27 19:21:53 -0400 |
commit | 022cc024da1e4ff5664742befed0059c1ddfa255 (patch) | |
tree | d945500195a5f1f032ac7deb1dbb9119809ca3ed /src/mongo/db | |
parent | 4ac0bfb55d9cccbc6784ded607eae14312ec9bcc (diff) | |
download | mongo-022cc024da1e4ff5664742befed0059c1ddfa255.tar.gz |
SERVER-16444 Only make BSONObj underlying WSM owned if not mmapv1.
Resolves the performance regression with mmapv1 introduced by 764e0c4
where documents were being copied in PlanStages that perform
invalidations.
Partially reverted changes to DeleteStage and UpdateStage because the
document to return is not guaranteed to be owned after
makeObjOwnedIfNeeded() is called when running with mmapv1.
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/exec/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/exec/and_common-inl.h | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/and_hash.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/exec/and_sorted.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/exec/cached_plan.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/delete.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/exec/fetch.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/merge_sort.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/multi_plan.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/near.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/sort.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/exec/text_or.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/exec/update.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/exec/working_set.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/exec/working_set.h | 4 |
15 files changed, 40 insertions, 24 deletions
diff --git a/src/mongo/db/exec/SConscript b/src/mongo/db/exec/SConscript index 6749b635a34..a501a19d56b 100644 --- a/src/mongo/db/exec/SConscript +++ b/src/mongo/db/exec/SConscript @@ -10,6 +10,7 @@ env.Library( ], LIBDEPS = [ "$BUILD_DIR/mongo/base", + "$BUILD_DIR/mongo/db/service_context", ], ) diff --git a/src/mongo/db/exec/and_common-inl.h b/src/mongo/db/exec/and_common-inl.h index 552b6312045..8151163e6d3 100644 --- a/src/mongo/db/exec/and_common-inl.h +++ b/src/mongo/db/exec/and_common-inl.h @@ -67,7 +67,7 @@ public: // 'src' has the full document but 'dest' doesn't so we need to copy it over. dest->obj = src.obj; - dest->makeObjOwned(); + dest->makeObjOwnedIfNeeded(); // We have an object so we don't need key data. dest->keyData.clear(); diff --git a/src/mongo/db/exec/and_hash.cpp b/src/mongo/db/exec/and_hash.cpp index e272c54b17e..4518802afba 100644 --- a/src/mongo/db/exec/and_hash.cpp +++ b/src/mongo/db/exec/and_hash.cpp @@ -147,7 +147,7 @@ PlanStage::StageState AndHashStage::work(WorkingSetID* out) { } else if (PlanStage::ADVANCED == childStatus) { // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we // yield. - _ws->get(_lookAheadResults[i])->makeObjOwned(); + _ws->get(_lookAheadResults[i])->makeObjOwnedIfNeeded(); break; // Stop looking at this child. } else if (PlanStage::FAILURE == childStatus || PlanStage::DEAD == childStatus) { // Propage error to parent. @@ -286,7 +286,7 @@ PlanStage::StageState AndHashStage::readFirstChild(WorkingSetID* out) { } // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); // Update memory stats. _memUsage += member->getMemUsage(); diff --git a/src/mongo/db/exec/and_sorted.cpp b/src/mongo/db/exec/and_sorted.cpp index 10c351f8e64..277d3424cf3 100644 --- a/src/mongo/db/exec/and_sorted.cpp +++ b/src/mongo/db/exec/and_sorted.cpp @@ -116,7 +116,7 @@ PlanStage::StageState AndSortedStage::getTargetLoc(WorkingSetID* out) { _targetLoc = member->loc; // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); // We have to AND with all other children. for (size_t i = 1; i < _children.size(); ++i) { @@ -215,7 +215,7 @@ PlanStage::StageState AndSortedStage::moveTowardTargetLoc(WorkingSetID* out) { _targetId = id; // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _workingTowardRep = std::queue<size_t>(); for (size_t i = 0; i < _children.size(); ++i) { diff --git a/src/mongo/db/exec/cached_plan.cpp b/src/mongo/db/exec/cached_plan.cpp index 147c74a32ec..a823a687792 100644 --- a/src/mongo/db/exec/cached_plan.cpp +++ b/src/mongo/db/exec/cached_plan.cpp @@ -98,7 +98,7 @@ Status CachedPlanStage::pickBestPlan(PlanYieldPolicy* yieldPolicy) { // Save result for later. WorkingSetMember* member = _ws->get(id); // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _results.push_back(id); if (_results.size() >= numResults) { diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp index b58354aa5d5..c491af42346 100644 --- a/src/mongo/db/exec/delete.cpp +++ b/src/mongo/db/exec/delete.cpp @@ -157,7 +157,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) { // Ensure that the BSONObj underlying the WorkingSetMember is owned because saveState() // is allowed to free the memory. if (_params.returnDeleted) { - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); } // TODO: Do we want to buffer docs and delete them in a group rather than @@ -175,6 +175,9 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) { } if (_params.returnDeleted) { + // Save a copy of the document that is about to get deleted. + BSONObj deletedDoc = member->obj.value(); + member->obj.setValue(deletedDoc.getOwned()); member->loc = RecordId(); member->transitionToOwnedObj(); } @@ -190,7 +193,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) { } catch (const WriteConflictException& wce) { // Ensure that the BSONObj underlying the WorkingSetMember is owned because it may be // freed when we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _idRetrying = id; memberFreer.Dismiss(); // Keep this member around so we can retry deleting it. *out = WorkingSet::INVALID_ID; diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp index c22a30b5c7e..6e972a9346e 100644 --- a/src/mongo/db/exec/fetch.cpp +++ b/src/mongo/db/exec/fetch.cpp @@ -130,7 +130,7 @@ PlanStage::StageState FetchStage::work(WorkingSetID* out) { } catch (const WriteConflictException& wce) { // Ensure that the BSONObj underlying the WorkingSetMember is owned because it may // be freed when we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _idRetrying = id; *out = WorkingSet::INVALID_ID; _commonStats.needYield++; diff --git a/src/mongo/db/exec/merge_sort.cpp b/src/mongo/db/exec/merge_sort.cpp index be218d266b5..98b6037d990 100644 --- a/src/mongo/db/exec/merge_sort.cpp +++ b/src/mongo/db/exec/merge_sort.cpp @@ -123,7 +123,7 @@ PlanStage::StageState MergeSortStage::work(WorkingSetID* out) { value.id = id; value.stage = child; // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _mergingData.push_front(value); // Insert the result (indirectly) into our priority queue. diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp index 7d3c636882c..a00ea97769c 100644 --- a/src/mongo/db/exec/multi_plan.cpp +++ b/src/mongo/db/exec/multi_plan.cpp @@ -382,7 +382,7 @@ bool MultiPlanStage::workAllPlans(size_t numResults, PlanYieldPolicy* yieldPolic WorkingSetMember* member = candidate.ws->get(id); // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we choose to // return the results from the 'candidate' plan. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); candidate.results.push_back(id); // Once a plan returns enough results, stop working. diff --git a/src/mongo/db/exec/near.cpp b/src/mongo/db/exec/near.cpp index 96386326841..2095c22ae45 100644 --- a/src/mongo/db/exec/near.cpp +++ b/src/mongo/db/exec/near.cpp @@ -217,7 +217,7 @@ PlanStage::StageState NearStage::bufferNext(WorkingSetID* toReturn, Status* erro double memberDistance = distanceStatus.getValue(); // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - nextMember->makeObjOwned(); + nextMember->makeObjOwnedIfNeeded(); _resultBuffer.push(SearchResult(nextMemberID, memberDistance)); // Store the member's RecordId, if available, for quick invalidation diff --git a/src/mongo/db/exec/sort.cpp b/src/mongo/db/exec/sort.cpp index 3295a6b82fe..6780e1bee82 100644 --- a/src/mongo/db/exec/sort.cpp +++ b/src/mongo/db/exec/sort.cpp @@ -274,12 +274,12 @@ void SortStage::addToBuffer(const SortableDataItem& item) { WorkingSetMember* member = _ws->get(item.wsid); if (_limit == 0) { // Ensure that the BSONObj underlying the WorkingSetMember is owned in case we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _data.push_back(item); _memUsage += member->getMemUsage(); } else if (_limit == 1) { if (_data.empty()) { - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _data.push_back(item); _memUsage = member->getMemUsage(); return; @@ -289,7 +289,7 @@ void SortStage::addToBuffer(const SortableDataItem& item) { // Compare new item with existing item in vector. if (cmp(item, _data[0])) { wsidToFree = _data[0].wsid; - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _data[0] = item; _memUsage = member->getMemUsage(); } @@ -298,7 +298,7 @@ void SortStage::addToBuffer(const SortableDataItem& item) { // Limit not reached - insert and return vector<SortableDataItem>::size_type limit(_limit); if (_dataSet->size() < limit) { - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _dataSet->insert(item); _memUsage += member->getMemUsage(); return; @@ -319,7 +319,7 @@ void SortStage::addToBuffer(const SortableDataItem& item) { // Here, we choose to erase first to release potential resources // used by the last item and to keep the scope of the iterator to a minimum. _dataSet->erase(lastItemIt); - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _dataSet->insert(item); } } diff --git a/src/mongo/db/exec/text_or.cpp b/src/mongo/db/exec/text_or.cpp index 3f525214747..d1090661f6e 100644 --- a/src/mongo/db/exec/text_or.cpp +++ b/src/mongo/db/exec/text_or.cpp @@ -334,7 +334,7 @@ private: WorkingSetMember* member = _ws->get(_id); // Make it owned since we are buffering results. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); return member->obj.value(); } @@ -375,7 +375,7 @@ PlanStage::StageState TextOrStage::addTerm(WorkingSetID wsid, WorkingSetID* out) } catch (const WriteConflictException& wce) { // Ensure that the BSONObj underlying the WorkingSetMember is owned because it may // be freed when we yield. - wsm->makeObjOwned(); + wsm->makeObjOwnedIfNeeded(); _idRetrying = wsid; *out = WorkingSet::INVALID_ID; return NEED_YIELD; diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp index db693ea99ba..ed9a341ac94 100644 --- a/src/mongo/db/exec/update.cpp +++ b/src/mongo/db/exec/update.cpp @@ -855,7 +855,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) { // Ensure that the BSONObj underlying the WorkingSetMember is owned because saveState() // is allowed to free the memory. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); // Save state before making changes try { @@ -869,6 +869,12 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) { std::terminate(); } + // If we care about the pre-updated version of the doc, save it out here. + BSONObj oldObj; + if (_params.request->shouldReturnOldDocs()) { + oldObj = member->obj.value().getOwned(); + } + // Do the update, get us the new version of the doc. BSONObj newObj = transformAndUpdate(member->obj, loc); @@ -877,6 +883,9 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) { if (_params.request->shouldReturnNewDocs()) { member->obj = Snapshotted<BSONObj>(getOpCtx()->recoveryUnit()->getSnapshotId(), newObj.getOwned()); + } else { + invariant(_params.request->shouldReturnOldDocs()); + member->obj.setValue(oldObj); } member->loc = RecordId(); member->transitionToOwnedObj(); @@ -884,7 +893,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) { } catch (const WriteConflictException& wce) { // Ensure that the BSONObj underlying the WorkingSetMember is owned because it may be // freed when we yield. - member->makeObjOwned(); + member->makeObjOwnedIfNeeded(); _idRetrying = id; memberFreer.Dismiss(); // Keep this member around so we can retry updating it. *out = WorkingSet::INVALID_ID; diff --git a/src/mongo/db/exec/working_set.cpp b/src/mongo/db/exec/working_set.cpp index c55e512fc32..caf6b8b918e 100644 --- a/src/mongo/db/exec/working_set.cpp +++ b/src/mongo/db/exec/working_set.cpp @@ -29,6 +29,7 @@ #include "mongo/db/exec/working_set.h" #include "mongo/db/index/index_descriptor.h" +#include "mongo/db/service_context.h" #include "mongo/db/storage/record_fetcher.h" namespace mongo { @@ -168,8 +169,8 @@ bool WorkingSetMember::hasOwnedObj() const { return _state == OWNED_OBJ || (_state == LOC_AND_OBJ && obj.value().isOwned()); } -void WorkingSetMember::makeObjOwned() { - if (_state == LOC_AND_OBJ && !obj.value().isOwned()) { +void WorkingSetMember::makeObjOwnedIfNeeded() { + if (supportsDocLocking() && _state == LOC_AND_OBJ && !obj.value().isOwned()) { obj.setValue(obj.value().getOwned()); } } diff --git a/src/mongo/db/exec/working_set.h b/src/mongo/db/exec/working_set.h index f10efca6d3b..a769fe395ea 100644 --- a/src/mongo/db/exec/working_set.h +++ b/src/mongo/db/exec/working_set.h @@ -293,8 +293,10 @@ public: /** * Ensures that 'obj' of a WSM in the LOC_AND_OBJ state is owned BSON. It is a no-op if the WSM * is in a different state or if 'obj' is already owned. + * + * It is also a no-op if the active storage engine doesn't support document-level concurrency. */ - void makeObjOwned(); + void makeObjOwnedIfNeeded(); // // Computed data |