summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2015-06-22 16:37:08 -0400
committerDavid Storch <david.storch@10gen.com>2015-07-01 14:42:17 -0400
commit820289c3c4244666e1829732150d842af775e56d (patch)
tree9844ae54f597514bd8fb49b9fe04831ae700770f
parent4f4e36b69bfd6ea8615e2c0786a4d1dbca502a74 (diff)
downloadmongo-820289c3c4244666e1829732150d842af775e56d.tar.gz
SERVER-18961 avoid iterating the entire working set when preparing for a WiredTiger snapshot change
Improves performance for query plans with a blocking stage when using the WiredTiger storage engine. In particular, full text search and geoNear queries should benefit.
-rw-r--r--src/mongo/db/commands/list_collections.cpp13
-rw-r--r--src/mongo/db/commands/list_indexes.cpp13
-rw-r--r--src/mongo/db/exec/and_common-inl.h16
-rw-r--r--src/mongo/db/exec/and_hash.cpp8
-rw-r--r--src/mongo/db/exec/and_sorted.cpp2
-rw-r--r--src/mongo/db/exec/collection_scan.cpp8
-rw-r--r--src/mongo/db/exec/collection_scan.h5
-rw-r--r--src/mongo/db/exec/delete.cpp10
-rw-r--r--src/mongo/db/exec/distinct_scan.cpp2
-rw-r--r--src/mongo/db/exec/fetch.cpp4
-rw-r--r--src/mongo/db/exec/fetch.h2
-rw-r--r--src/mongo/db/exec/group.cpp2
-rw-r--r--src/mongo/db/exec/idhack.cpp8
-rw-r--r--src/mongo/db/exec/index_scan.cpp2
-rw-r--r--src/mongo/db/exec/multi_iterator.cpp10
-rw-r--r--src/mongo/db/exec/multi_iterator.h5
-rw-r--r--src/mongo/db/exec/oplogstart.cpp2
-rw-r--r--src/mongo/db/exec/pipeline_proxy.cpp4
-rw-r--r--src/mongo/db/exec/projection.cpp2
-rw-r--r--src/mongo/db/exec/projection_exec.cpp6
-rw-r--r--src/mongo/db/exec/projection_exec_test.cpp2
-rw-r--r--src/mongo/db/exec/queued_data_stage.cpp6
-rw-r--r--src/mongo/db/exec/queued_data_stage.h9
-rw-r--r--src/mongo/db/exec/queued_data_stage_test.cpp4
-rw-r--r--src/mongo/db/exec/sort_test.cpp11
-rw-r--r--src/mongo/db/exec/text.cpp29
-rw-r--r--src/mongo/db/exec/update.cpp12
-rw-r--r--src/mongo/db/exec/working_set.cpp105
-rw-r--r--src/mongo/db/exec/working_set.h117
-rw-r--r--src/mongo/db/exec/working_set_common.cpp51
-rw-r--r--src/mongo/db/exec/working_set_common.h34
-rw-r--r--src/mongo/db/exec/working_set_test.cpp120
-rw-r--r--src/mongo/db/query/plan_executor.cpp2
-rw-r--r--src/mongo/db/query/query_solution.h3
-rw-r--r--src/mongo/dbtests/query_stage_and.cpp59
-rw-r--r--src/mongo/dbtests/query_stage_delete.cpp30
-rw-r--r--src/mongo/dbtests/query_stage_fetch.cpp37
-rw-r--r--src/mongo/dbtests/query_stage_ixscan.cpp18
-rw-r--r--src/mongo/dbtests/query_stage_keep.cpp6
-rw-r--r--src/mongo/dbtests/query_stage_limit_skip.cpp11
-rw-r--r--src/mongo/dbtests/query_stage_near.cpp2
-rw-r--r--src/mongo/dbtests/query_stage_sort.cpp41
-rw-r--r--src/mongo/dbtests/query_stage_update.cpp49
43 files changed, 395 insertions, 487 deletions
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp
index a97bdbc202a..a5b12e97f9c 100644
--- a/src/mongo/db/commands/list_collections.cpp
+++ b/src/mongo/db/commands/list_collections.cpp
@@ -157,12 +157,13 @@ public:
continue;
}
- WorkingSetMember member;
- member.state = WorkingSetMember::OWNED_OBJ;
- member.keyData.clear();
- member.loc = RecordId();
- member.obj = Snapshotted<BSONObj>(SnapshotId(), maybe);
- root->pushBack(member);
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->keyData.clear();
+ member->loc = RecordId();
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), maybe);
+ member->transitionToOwnedObj();
+ root->pushBack(id);
}
std::string cursorNamespace = str::stream() << dbname << ".$cmd." << name;
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index a22d3b43f62..06d08e9cce0 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -153,12 +153,13 @@ public:
}
MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "listIndexes", ns.ns());
- WorkingSetMember member;
- member.state = WorkingSetMember::OWNED_OBJ;
- member.keyData.clear();
- member.loc = RecordId();
- member.obj = Snapshotted<BSONObj>(SnapshotId(), indexSpec.getOwned());
- root->pushBack(member);
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->keyData.clear();
+ member->loc = RecordId();
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), indexSpec.getOwned());
+ member->transitionToOwnedObj();
+ root->pushBack(id);
}
std::string cursorNamespace = str::stream() << dbname << ".$cmd." << name << "."
diff --git a/src/mongo/db/exec/and_common-inl.h b/src/mongo/db/exec/and_common-inl.h
index c4be4684078..85599c198fc 100644
--- a/src/mongo/db/exec/and_common-inl.h
+++ b/src/mongo/db/exec/and_common-inl.h
@@ -33,9 +33,14 @@ namespace mongo {
class AndCommon {
public:
/**
- * If src has any data dest doesn't, add that data to dest.
+ * If 'src' has any data that the member in 'workingSet' keyed by 'destId' doesn't, add that
+ * data to 'destId's WSM.
*/
- static void mergeFrom(WorkingSetMember* dest, const WorkingSetMember& src) {
+ static void mergeFrom(WorkingSet* workingSet,
+ WorkingSetID destId,
+ const WorkingSetMember& src) {
+ WorkingSetMember* dest = workingSet->get(destId);
+
// Both 'src' and 'dest' must have a RecordId (and they must be the same RecordId), as
// we should have just matched them according to this RecordId while doing an
// intersection.
@@ -58,16 +63,15 @@ public:
}
if (src.hasObj()) {
+ invariant(src.getState() == WorkingSetMember::LOC_AND_OBJ);
+
// 'src' has the full document but 'dest' doesn't so we need to copy it over.
dest->obj = src.obj;
// We have an object so we don't need key data.
dest->keyData.clear();
- // 'dest' should have the same state as 'src'. If 'src' has an unowned obj, then
- // 'dest' also should have an unowned obj; if 'src' has an owned obj, then dest
- // should also have an owned obj.
- dest->state = src.state;
+ workingSet->transitionToLocAndObj(destId);
// Now 'dest' has the full object. No more work to do.
return;
diff --git a/src/mongo/db/exec/and_hash.cpp b/src/mongo/db/exec/and_hash.cpp
index eb3d6412ca5..9fe0111ebe8 100644
--- a/src/mongo/db/exec/and_hash.cpp
+++ b/src/mongo/db/exec/and_hash.cpp
@@ -242,8 +242,7 @@ PlanStage::StageState AndHashStage::work(WorkingSetID* out) {
WorkingSetID hashID = it->second;
_dataMap.erase(it);
- WorkingSetMember* olderMember = _ws->get(hashID);
- AndCommon::mergeFrom(olderMember, *member);
+ AndCommon::mergeFrom(_ws, hashID, *member);
_ws->free(*out);
++_commonStats.advanced;
@@ -352,10 +351,11 @@ PlanStage::StageState AndHashStage::hashOtherChildren(WorkingSetID* out) {
} else {
// We have a hit. Copy data into the WSM we already have.
_seenMap.insert(member->loc);
- WorkingSetMember* olderMember = _ws->get(_dataMap[member->loc]);
+ WorkingSetID olderMemberID = _dataMap[member->loc];
+ WorkingSetMember* olderMember = _ws->get(olderMemberID);
size_t memUsageBefore = olderMember->getMemUsage();
- AndCommon::mergeFrom(olderMember, *member);
+ AndCommon::mergeFrom(_ws, olderMemberID, *member);
// Update memory stats.
_memUsage += olderMember->getMemUsage() - memUsageBefore;
diff --git a/src/mongo/db/exec/and_sorted.cpp b/src/mongo/db/exec/and_sorted.cpp
index 0d9beeea8bb..7186e694daa 100644
--- a/src/mongo/db/exec/and_sorted.cpp
+++ b/src/mongo/db/exec/and_sorted.cpp
@@ -180,7 +180,7 @@ PlanStage::StageState AndSortedStage::moveTowardTargetLoc(WorkingSetID* out) {
// The front element has hit _targetLoc. Don't move it forward anymore/work on
// another element.
_workingTowardRep.pop();
- AndCommon::mergeFrom(_ws->get(_targetId), *member);
+ AndCommon::mergeFrom(_ws, _targetId, *member);
_ws->free(id);
if (0 == _workingTowardRep.size()) {
diff --git a/src/mongo/db/exec/collection_scan.cpp b/src/mongo/db/exec/collection_scan.cpp
index d7e138b99c8..22e72d0557a 100644
--- a/src/mongo/db/exec/collection_scan.cpp
+++ b/src/mongo/db/exec/collection_scan.cpp
@@ -67,12 +67,6 @@ CollectionScan::CollectionScan(OperationContext* txn,
_commonStats(kStageType) {
// Explain reports the direction of the collection scan.
_specificStats.direction = params.direction;
-
- // We pre-allocate a WSM and use it to pass up fetch requests. This should never be used
- // for anything other than passing up NEED_YIELD. We use the loc and owned obj state, but
- // the loc isn't really pointing at any obj. The obj field of the WSM should never be used.
- WorkingSetMember* member = _workingSet->get(_wsidForFetch);
- member->state = WorkingSetMember::LOC_AND_OWNED_OBJ;
}
PlanStage::StageState CollectionScan::work(WorkingSetID* out) {
@@ -166,7 +160,7 @@ PlanStage::StageState CollectionScan::work(WorkingSetID* out) {
WorkingSetMember* member = _workingSet->get(id);
member->loc = record->id;
member->obj = {_txn->recoveryUnit()->getSnapshotId(), record->data.releaseToBson()};
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ _workingSet->transitionToLocAndObj(id);
return returnIfMatches(member, id, out);
}
diff --git a/src/mongo/db/exec/collection_scan.h b/src/mongo/db/exec/collection_scan.h
index e2a3349847f..418a3b5b7e2 100644
--- a/src/mongo/db/exec/collection_scan.h
+++ b/src/mongo/db/exec/collection_scan.h
@@ -99,8 +99,9 @@ private:
RecordId _lastSeenId; // Null if nothing has been returned from _cursor yet.
- // We allocate a working set member with this id on construction of the stage. It gets
- // used for all fetch requests, changing the RecordId as appropriate.
+ // We allocate a working set member with this id on construction of the stage. It gets used for
+ // all fetch requests. This should only be used for passing up the Fetcher for a NEED_YIELD, and
+ // should remain in the INVALID state.
const WorkingSetID _wsidForFetch;
// Stats
diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp
index 7ff695d2180..e4a89d197c0 100644
--- a/src/mongo/db/exec/delete.cpp
+++ b/src/mongo/db/exec/delete.cpp
@@ -98,7 +98,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
invariant(_params.returnDeleted);
WorkingSetMember* member = _ws->get(_idReturning);
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ invariant(member->getState() == WorkingSetMember::OWNED_OBJ);
*out = _idReturning;
_idReturning = WorkingSet::INVALID_ID;
@@ -141,7 +141,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
std::unique_ptr<RecordCursor> cursor;
if (_txn->recoveryUnit()->getSnapshotId() != member->obj.snapshotId()) {
cursor = _collection->getCursor(_txn);
- if (!WorkingSetCommon::fetch(_txn, member, cursor)) {
+ if (!WorkingSetCommon::fetch(_txn, _ws, id, cursor)) {
// Doc is already deleted. Nothing more to do.
++_commonStats.needTime;
return PlanStage::NEED_TIME;
@@ -175,7 +175,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
BSONObj deletedDoc = member->obj.value();
member->obj.setValue(deletedDoc.getOwned());
member->loc = RecordId();
- member->state = WorkingSetMember::OWNED_OBJ;
+ member->transitionToOwnedObj();
}
// Do the write, unless this is an explain.
@@ -215,7 +215,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
// (if it was requested).
if (_params.returnDeleted) {
// member->obj should refer to the deleted document.
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ invariant(member->getState() == WorkingSetMember::OWNED_OBJ);
_idReturning = id;
// Keep this member around so that we can return it on the next work() call.
@@ -228,7 +228,7 @@ PlanStage::StageState DeleteStage::work(WorkingSetID* out) {
if (_params.returnDeleted) {
// member->obj should refer to the deleted document.
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ invariant(member->getState() == WorkingSetMember::OWNED_OBJ);
memberFreer.Dismiss(); // Keep this member around so we can return it.
*out = id;
diff --git a/src/mongo/db/exec/distinct_scan.cpp b/src/mongo/db/exec/distinct_scan.cpp
index 82e87b180c0..e189861dc86 100644
--- a/src/mongo/db/exec/distinct_scan.cpp
+++ b/src/mongo/db/exec/distinct_scan.cpp
@@ -115,7 +115,7 @@ PlanStage::StageState DistinctScan::work(WorkingSetID* out) {
WorkingSetMember* member = _workingSet->get(id);
member->loc = kv->loc;
member->keyData.push_back(IndexKeyDatum(_descriptor->keyPattern(), kv->key, _iam));
- member->state = WorkingSetMember::LOC_AND_IDX;
+ _workingSet->transitionToLocAndIdx(id);
*out = id;
++_commonStats.advanced;
diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp
index c78ac905414..eae3b00a361 100644
--- a/src/mongo/db/exec/fetch.cpp
+++ b/src/mongo/db/exec/fetch.cpp
@@ -103,7 +103,7 @@ PlanStage::StageState FetchStage::work(WorkingSetID* out) {
++_specificStats.alreadyHasObj;
} else {
// We need a valid loc to fetch from and this is the only state that has one.
- verify(WorkingSetMember::LOC_AND_IDX == member->state);
+ verify(WorkingSetMember::LOC_AND_IDX == member->getState());
verify(member->hasLoc());
try {
@@ -122,7 +122,7 @@ PlanStage::StageState FetchStage::work(WorkingSetID* out) {
// The doc is already in memory, so go ahead and grab it. Now we have a RecordId
// as well as an unowned object
- if (!WorkingSetCommon::fetch(_txn, member, _cursor)) {
+ if (!WorkingSetCommon::fetch(_txn, _ws, id, _cursor)) {
_ws->free(id);
_commonStats.needTime++;
return NEED_TIME;
diff --git a/src/mongo/db/exec/fetch.h b/src/mongo/db/exec/fetch.h
index 292f3bac6b8..9a0ff81c021 100644
--- a/src/mongo/db/exec/fetch.h
+++ b/src/mongo/db/exec/fetch.h
@@ -42,7 +42,7 @@ class RecordCursor;
/**
* This stage turns a RecordId into a BSONObj.
*
- * In WorkingSetMember terms, it transitions from LOC_AND_IDX to LOC_AND_UNOWNED_OBJ by reading
+ * In WorkingSetMember terms, it transitions from LOC_AND_IDX to LOC_AND_OBJ by reading
* the record at the provided loc. Returns verbatim any data that already has an object.
*
* Preconditions: Valid RecordId.
diff --git a/src/mongo/db/exec/group.cpp b/src/mongo/db/exec/group.cpp
index f9b657db9f8..8c945ef11d0 100644
--- a/src/mongo/db/exec/group.cpp
+++ b/src/mongo/db/exec/group.cpp
@@ -245,7 +245,7 @@ PlanStage::StageState GroupStage::work(WorkingSetID* out) {
*out = _ws->allocate();
WorkingSetMember* member = _ws->get(*out);
member->obj = Snapshotted<BSONObj>(SnapshotId(), results);
- member->state = WorkingSetMember::OWNED_OBJ;
+ member->transitionToOwnedObj();
++_commonStats.advanced;
return PlanStage::ADVANCED;
diff --git a/src/mongo/db/exec/idhack.cpp b/src/mongo/db/exec/idhack.cpp
index 51346ecfeb3..8e9e290b624 100644
--- a/src/mongo/db/exec/idhack.cpp
+++ b/src/mongo/db/exec/idhack.cpp
@@ -107,10 +107,10 @@ PlanStage::StageState IDHackStage::work(WorkingSetID* out) {
invariant(_recordCursor);
WorkingSetID id = _idBeingPagedIn;
_idBeingPagedIn = WorkingSet::INVALID_ID;
- WorkingSetMember* member = _workingSet->get(id);
- invariant(WorkingSetCommon::fetchIfUnfetched(_txn, member, _recordCursor));
+ invariant(WorkingSetCommon::fetchIfUnfetched(_txn, _workingSet, id, _recordCursor));
+ WorkingSetMember* member = _workingSet->get(id);
return advance(id, member, out);
}
@@ -141,8 +141,8 @@ PlanStage::StageState IDHackStage::work(WorkingSetID* out) {
// Create a new WSM for the result document.
id = _workingSet->allocate();
WorkingSetMember* member = _workingSet->get(id);
- member->state = WorkingSetMember::LOC_AND_IDX;
member->loc = loc;
+ _workingSet->transitionToLocAndIdx(id);
if (!_recordCursor)
_recordCursor = _collection->getCursor(_txn);
@@ -159,7 +159,7 @@ PlanStage::StageState IDHackStage::work(WorkingSetID* out) {
}
// The doc was already in memory, so we go ahead and return it.
- if (!WorkingSetCommon::fetch(_txn, member, _recordCursor)) {
+ if (!WorkingSetCommon::fetch(_txn, _workingSet, id, _recordCursor)) {
// _id is immutable so the index would return the only record that could
// possibly match the query.
_workingSet->free(id);
diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp
index 396c8a32f54..e994404960a 100644
--- a/src/mongo/db/exec/index_scan.cpp
+++ b/src/mongo/db/exec/index_scan.cpp
@@ -216,7 +216,7 @@ PlanStage::StageState IndexScan::work(WorkingSetID* out) {
WorkingSetMember* member = _workingSet->get(id);
member->loc = kv->loc;
member->keyData.push_back(IndexKeyDatum(_keyPattern, kv->key, _iam));
- member->state = WorkingSetMember::LOC_AND_IDX;
+ _workingSet->transitionToLocAndIdx(id);
if (_params.addKeyMetadata) {
BSONObjBuilder bob;
diff --git a/src/mongo/db/exec/multi_iterator.cpp b/src/mongo/db/exec/multi_iterator.cpp
index d2c79b24ce8..84ba4e76578 100644
--- a/src/mongo/db/exec/multi_iterator.cpp
+++ b/src/mongo/db/exec/multi_iterator.cpp
@@ -46,13 +46,7 @@ const char* MultiIteratorStage::kStageType = "MULTI_ITERATOR";
MultiIteratorStage::MultiIteratorStage(OperationContext* txn,
WorkingSet* ws,
Collection* collection)
- : _txn(txn), _collection(collection), _ws(ws), _wsidForFetch(_ws->allocate()) {
- // We pre-allocate a WSM and use it to pass up fetch requests. This should never be used
- // for anything other than passing up NEED_YIELD. We use the loc and owned obj state, but
- // the loc isn't really pointing at any obj. The obj field of the WSM should never be used.
- WorkingSetMember* member = _ws->get(_wsidForFetch);
- member->state = WorkingSetMember::LOC_AND_OWNED_OBJ;
-}
+ : _txn(txn), _collection(collection), _ws(ws), _wsidForFetch(_ws->allocate()) {}
void MultiIteratorStage::addIterator(unique_ptr<RecordCursor> it) {
_iterators.push_back(std::move(it));
@@ -95,7 +89,7 @@ PlanStage::StageState MultiIteratorStage::work(WorkingSetID* out) {
WorkingSetMember* member = _ws->get(*out);
member->loc = record->id;
member->obj = {_txn->recoveryUnit()->getSnapshotId(), record->data.releaseToBson()};
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ _ws->transitionToLocAndObj(*out);
return PlanStage::ADVANCED;
}
diff --git a/src/mongo/db/exec/multi_iterator.h b/src/mongo/db/exec/multi_iterator.h
index 011eb807bf7..9fe8f5338c8 100644
--- a/src/mongo/db/exec/multi_iterator.h
+++ b/src/mongo/db/exec/multi_iterator.h
@@ -95,8 +95,9 @@ private:
// Not owned by us.
WorkingSet* _ws;
- // We allocate a working set member with this id on construction of the stage. It gets
- // used for all fetch requests, changing the RecordId as appropriate.
+ // We allocate a working set member with this id on construction of the stage. It gets used for
+ // all fetch requests. This should only be used for passing up the Fetcher for a NEED_YIELD, and
+ // should remain in the INVALID state.
const WorkingSetID _wsidForFetch;
};
diff --git a/src/mongo/db/exec/oplogstart.cpp b/src/mongo/db/exec/oplogstart.cpp
index 3bff2fb885c..e077d448067 100644
--- a/src/mongo/db/exec/oplogstart.cpp
+++ b/src/mongo/db/exec/oplogstart.cpp
@@ -110,7 +110,7 @@ PlanStage::StageState OplogStart::workExtentHopping(WorkingSetID* out) {
WorkingSetMember* member = _workingSet->get(id);
member->loc = record->id;
member->obj = {_txn->recoveryUnit()->getSnapshotId(), std::move(obj)};
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ _workingSet->transitionToLocAndObj(id);
*out = id;
return PlanStage::ADVANCED;
}
diff --git a/src/mongo/db/exec/pipeline_proxy.cpp b/src/mongo/db/exec/pipeline_proxy.cpp
index 95a95f3cbb6..3aed8d3b4fb 100644
--- a/src/mongo/db/exec/pipeline_proxy.cpp
+++ b/src/mongo/db/exec/pipeline_proxy.cpp
@@ -64,7 +64,7 @@ PlanStage::StageState PipelineProxyStage::work(WorkingSetID* out) {
WorkingSetMember* member = _ws->get(*out);
member->obj = Snapshotted<BSONObj>(SnapshotId(), _stash.back());
_stash.pop_back();
- member->state = WorkingSetMember::OWNED_OBJ;
+ member->transitionToOwnedObj();
return PlanStage::ADVANCED;
}
@@ -72,7 +72,7 @@ PlanStage::StageState PipelineProxyStage::work(WorkingSetID* out) {
*out = _ws->allocate();
WorkingSetMember* member = _ws->get(*out);
member->obj = Snapshotted<BSONObj>(SnapshotId(), *next);
- member->state = WorkingSetMember::OWNED_OBJ;
+ member->transitionToOwnedObj();
return PlanStage::ADVANCED;
}
diff --git a/src/mongo/db/exec/projection.cpp b/src/mongo/db/exec/projection.cpp
index 50c2f71239e..4a10248e6a9 100644
--- a/src/mongo/db/exec/projection.cpp
+++ b/src/mongo/db/exec/projection.cpp
@@ -182,10 +182,10 @@ Status ProjectionStage::transform(WorkingSetMember* member) {
}
}
- member->state = WorkingSetMember::OWNED_OBJ;
member->keyData.clear();
member->loc = RecordId();
member->obj = Snapshotted<BSONObj>(SnapshotId(), bob.obj());
+ member->transitionToOwnedObj();
return Status::OK();
}
diff --git a/src/mongo/db/exec/projection_exec.cpp b/src/mongo/db/exec/projection_exec.cpp
index 80a2d8772af..2ce77a120d6 100644
--- a/src/mongo/db/exec/projection_exec.cpp
+++ b/src/mongo/db/exec/projection_exec.cpp
@@ -231,10 +231,10 @@ Status ProjectionExec::transform(WorkingSetMember* member) const {
keyObj = key->getKey();
}
- member->state = WorkingSetMember::OWNED_OBJ;
- member->obj = Snapshotted<BSONObj>(SnapshotId(), keyObj);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), keyObj.getOwned());
member->keyData.clear();
member->loc = RecordId();
+ member->transitionToOwnedObj();
return Status::OK();
}
@@ -318,10 +318,10 @@ Status ProjectionExec::transform(WorkingSetMember* member) const {
}
BSONObj newObj = bob.obj();
- member->state = WorkingSetMember::OWNED_OBJ;
member->obj = Snapshotted<BSONObj>(SnapshotId(), newObj);
member->keyData.clear();
member->loc = RecordId();
+ member->transitionToOwnedObj();
return Status::OK();
}
diff --git a/src/mongo/db/exec/projection_exec_test.cpp b/src/mongo/db/exec/projection_exec_test.cpp
index fca1065cba0..8575591e6d1 100644
--- a/src/mongo/db/exec/projection_exec_test.cpp
+++ b/src/mongo/db/exec/projection_exec_test.cpp
@@ -85,11 +85,11 @@ void testTransform(const char* specStr,
// Create working set member.
WorkingSetMember wsm;
- wsm.state = WorkingSetMember::OWNED_OBJ;
wsm.obj = Snapshotted<BSONObj>(SnapshotId(), fromjson(objStr));
if (data) {
wsm.addComputed(data);
}
+ wsm.transitionToOwnedObj();
// Transform object
Status status = exec.transform(&wsm);
diff --git a/src/mongo/db/exec/queued_data_stage.cpp b/src/mongo/db/exec/queued_data_stage.cpp
index 031460dc394..e51ae60c347 100644
--- a/src/mongo/db/exec/queued_data_stage.cpp
+++ b/src/mongo/db/exec/queued_data_stage.cpp
@@ -102,13 +102,9 @@ void QueuedDataStage::pushBack(const PlanStage::StageState state) {
_results.push(state);
}
-void QueuedDataStage::pushBack(const WorkingSetMember& member) {
+void QueuedDataStage::pushBack(const WorkingSetID& id) {
_results.push(PlanStage::ADVANCED);
- WorkingSetID id = _ws->allocate();
- WorkingSetMember* ourMember = _ws->get(id);
- WorkingSetCommon::initFrom(ourMember, member);
-
// member lives in _ws. We'll return it when _results hits ADVANCED.
_members.push(id);
}
diff --git a/src/mongo/db/exec/queued_data_stage.h b/src/mongo/db/exec/queued_data_stage.h
index 62a2d8d7bac..ec93eb1dfb3 100644
--- a/src/mongo/db/exec/queued_data_stage.h
+++ b/src/mongo/db/exec/queued_data_stage.h
@@ -91,10 +91,13 @@ public:
/**
* ...data is returned (and we ADVANCED)
*
- * Allocates a new member and copies 'member' into it.
- * Does not take ownership of anything in 'member'.
+ * The caller is responsible for allocating 'id' and filling out the WSM keyed by 'id'
+ * appropriately.
+ *
+ * The QueuedDataStage takes ownership of 'id', so the caller should not call WorkingSet::free()
+ * on it.
*/
- void pushBack(const WorkingSetMember& member);
+ void pushBack(const WorkingSetID& id);
static const char* kStageType;
diff --git a/src/mongo/db/exec/queued_data_stage_test.cpp b/src/mongo/db/exec/queued_data_stage_test.cpp
index 45e4a6b2a96..3b2e89f2577 100644
--- a/src/mongo/db/exec/queued_data_stage_test.cpp
+++ b/src/mongo/db/exec/queued_data_stage_test.cpp
@@ -80,8 +80,8 @@ TEST(QueuedDataStageTest, validateStats) {
ASSERT_EQUALS(stats->needTime, 1U);
// advanced, with pushed data
- const WorkingSetMember member;
- mock->pushBack(member);
+ WorkingSetID id = ws.allocate();
+ mock->pushBack(id);
mock->work(&wsID);
ASSERT_EQUALS(stats->works, 2U);
ASSERT_EQUALS(stats->advanced, 1U);
diff --git a/src/mongo/db/exec/sort_test.cpp b/src/mongo/db/exec/sort_test.cpp
index 0d4cc891952..a60146ef1f9 100644
--- a/src/mongo/db/exec/sort_test.cpp
+++ b/src/mongo/db/exec/sort_test.cpp
@@ -95,13 +95,14 @@ void testWork(const char* patternStr,
while (inputIt.more()) {
BSONElement elt = inputIt.next();
ASSERT(elt.isABSONObj());
- BSONObj obj = elt.embeddedObject();
+ BSONObj obj = elt.embeddedObject().getOwned();
// Insert obj from input array into working set.
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::OWNED_OBJ;
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), obj);
- ms->pushBack(wsm);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* wsm = ws.get(id);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), obj);
+ wsm->transitionToOwnedObj();
+ ms->pushBack(id);
}
// Initialize SortStageParams
diff --git a/src/mongo/db/exec/text.cpp b/src/mongo/db/exec/text.cpp
index 2f2a41726e8..5c4123e29cf 100644
--- a/src/mongo/db/exec/text.cpp
+++ b/src/mongo/db/exec/text.cpp
@@ -308,7 +308,7 @@ PlanStage::StageState TextStage::returnResults(WorkingSetID* out) {
WorkingSetMember* wsm = _ws->get(textRecordData.wsid);
try {
- if (!WorkingSetCommon::fetchIfUnfetched(_txn, wsm, _recordCursor)) {
+ if (!WorkingSetCommon::fetchIfUnfetched(_txn, _ws, textRecordData.wsid, _recordCursor)) {
_scoreIterator++;
_ws->free(textRecordData.wsid);
_commonStats.needTime++;
@@ -340,16 +340,23 @@ public:
TextMatchableDocument(OperationContext* txn,
const BSONObj& keyPattern,
const BSONObj& key,
- WorkingSetMember* wsm,
+ WorkingSet* ws,
+ WorkingSetID id,
unowned_ptr<RecordCursor> recordCursor)
- : _txn(txn), _recordCursor(recordCursor), _keyPattern(keyPattern), _key(key), _wsm(wsm) {}
+ : _txn(txn),
+ _recordCursor(recordCursor),
+ _keyPattern(keyPattern),
+ _key(key),
+ _ws(ws),
+ _id(id) {}
BSONObj toBSON() const {
return getObj();
}
virtual ElementIterator* allocateIterator(const ElementPath* path) const {
- if (!_wsm->hasObj()) {
+ WorkingSetMember* member = _ws->get(_id);
+ if (!member->hasObj()) {
// Try to look in the key.
BSONObjIterator keyPatternIt(_keyPattern);
BSONObjIterator keyDataIt(_key);
@@ -382,24 +389,26 @@ public:
private:
BSONObj getObj() const {
- if (!WorkingSetCommon::fetchIfUnfetched(_txn, _wsm, _recordCursor))
+ if (!WorkingSetCommon::fetchIfUnfetched(_txn, _ws, _id, _recordCursor))
throw DocumentDeletedException();
+ WorkingSetMember* member = _ws->get(_id);
// Make it owned since we are buffering results.
- _wsm->obj.setValue(_wsm->obj.value().getOwned());
- return _wsm->obj.value();
+ member->obj.setValue(member->obj.value().getOwned());
+ return member->obj.value();
}
OperationContext* _txn;
unowned_ptr<RecordCursor> _recordCursor;
BSONObj _keyPattern;
BSONObj _key;
- WorkingSetMember* _wsm;
+ WorkingSet* _ws;
+ WorkingSetID _id;
};
PlanStage::StageState TextStage::addTerm(WorkingSetID wsid, WorkingSetID* out) {
WorkingSetMember* wsm = _ws->get(wsid);
- invariant(wsm->state == WorkingSetMember::LOC_AND_IDX);
+ invariant(wsm->getState() == WorkingSetMember::LOC_AND_IDX);
invariant(1 == wsm->keyData.size());
const IndexKeyDatum newKeyData = wsm->keyData.back(); // copy to keep it around.
@@ -417,7 +426,7 @@ PlanStage::StageState TextStage::addTerm(WorkingSetID wsid, WorkingSetID* out) {
bool wasDeleted = false;
try {
TextMatchableDocument tdoc(
- _txn, newKeyData.indexKeyPattern, newKeyData.keyData, wsm, _recordCursor);
+ _txn, newKeyData.indexKeyPattern, newKeyData.keyData, _ws, wsid, _recordCursor);
shouldKeep = _filter->matches(&tdoc);
} catch (const WriteConflictException& wce) {
_idRetrying = wsid;
diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp
index f1552888fdb..9a523c4756b 100644
--- a/src/mongo/db/exec/update.cpp
+++ b/src/mongo/db/exec/update.cpp
@@ -759,7 +759,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) {
WorkingSetMember* member = _ws->get(*out);
member->obj =
Snapshotted<BSONObj>(_txn->recoveryUnit()->getSnapshotId(), newObj.getOwned());
- member->state = WorkingSetMember::OWNED_OBJ;
+ member->transitionToOwnedObj();
++_commonStats.advanced;
return PlanStage::ADVANCED;
}
@@ -783,7 +783,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) {
invariant(_params.request->shouldReturnAnyDocs());
WorkingSetMember* member = _ws->get(_idReturning);
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ invariant(member->getState() == WorkingSetMember::OWNED_OBJ);
*out = _idReturning;
_idReturning = WorkingSet::INVALID_ID;
@@ -838,7 +838,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) {
if (_txn->recoveryUnit()->getSnapshotId() != member->obj.snapshotId()) {
cursor = _collection->getCursor(_txn);
// our snapshot has changed, refetch
- if (!WorkingSetCommon::fetch(_txn, member, cursor)) {
+ if (!WorkingSetCommon::fetch(_txn, _ws, id, cursor)) {
// document was deleted, we're done here
++_commonStats.needTime;
return PlanStage::NEED_TIME;
@@ -884,7 +884,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) {
member->obj.setValue(oldObj);
}
member->loc = RecordId();
- member->state = WorkingSetMember::OWNED_OBJ;
+ member->transitionToOwnedObj();
}
} catch (const WriteConflictException& wce) {
_idRetrying = id;
@@ -909,7 +909,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) {
// (if it was requested).
if (_params.request->shouldReturnAnyDocs()) {
// member->obj should refer to the document we want to return.
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ invariant(member->getState() == WorkingSetMember::OWNED_OBJ);
_idReturning = id;
// Keep this member around so that we can return it on the next work() call.
@@ -922,7 +922,7 @@ PlanStage::StageState UpdateStage::work(WorkingSetID* out) {
if (_params.request->shouldReturnAnyDocs()) {
// member->obj should refer to the document we want to return.
- invariant(member->state == WorkingSetMember::OWNED_OBJ);
+ invariant(member->getState() == WorkingSetMember::OWNED_OBJ);
memberFreer.Dismiss(); // Keep this member around so we can return it.
*out = id;
diff --git a/src/mongo/db/exec/working_set.cpp b/src/mongo/db/exec/working_set.cpp
index 5e531af346a..0eae502e3cc 100644
--- a/src/mongo/db/exec/working_set.cpp
+++ b/src/mongo/db/exec/working_set.cpp
@@ -65,7 +65,7 @@ WorkingSetID WorkingSet::allocate() {
return id;
}
-void WorkingSet::free(const WorkingSetID& i) {
+void WorkingSet::free(WorkingSetID i) {
MemberHolder& holder = _data[i];
verify(i < _data.size()); // ID has been allocated.
verify(holder.nextFreeOrSelf == i); // ID currently in use.
@@ -76,9 +76,9 @@ void WorkingSet::free(const WorkingSetID& i) {
_freeList = i;
}
-void WorkingSet::flagForReview(const WorkingSetID& i) {
+void WorkingSet::flagForReview(WorkingSetID i) {
WorkingSetMember* member = get(i);
- verify(WorkingSetMember::OWNED_OBJ == member->state);
+ verify(WorkingSetMember::OWNED_OBJ == member->_state);
_flagged.insert(i);
}
@@ -102,76 +102,42 @@ void WorkingSet::clear() {
_freeList = INVALID_ID;
_flagged.clear();
+ _yieldSensitiveIds.clear();
}
-//
-// Iteration
-//
-
-WorkingSet::iterator::iterator(WorkingSet* ws, size_t index) : _ws(ws), _index(index) {
- // If we're currently not pointing at an allocated member, then we have
- // to advance to the first one, unless we're already at the end.
- if (_index < _ws->_data.size() && isFree()) {
- advance();
- }
+void WorkingSet::transitionToLocAndIdx(WorkingSetID id) {
+ WorkingSetMember* member = get(id);
+ member->_state = WorkingSetMember::LOC_AND_IDX;
+ _yieldSensitiveIds.push_back(id);
}
-void WorkingSet::iterator::advance() {
- // Move forward at least once in the data list.
- _index++;
+void WorkingSet::transitionToLocAndObj(WorkingSetID id) {
+ WorkingSetMember* member = get(id);
+ member->_state = WorkingSetMember::LOC_AND_OBJ;
- // While we haven't hit the end and the current member is not in use. (Skips ahead until
- // we find the next allocated member.)
- while (_index < _ws->_data.size() && isFree()) {
- _index++;
+ // If 'obj' is owned, then it doesn't need to made owned in preparation for yield.
+ if (!member->obj.value().isOwned()) {
+ _yieldSensitiveIds.push_back(id);
}
}
-bool WorkingSet::iterator::isFree() const {
- return _ws->_data[_index].nextFreeOrSelf != _index;
-}
-
-void WorkingSet::iterator::free() {
- dassert(!isFree());
- _ws->free(_index);
-}
-
-void WorkingSet::iterator::operator++() {
- dassert(_index < _ws->_data.size());
- advance();
-}
-
-bool WorkingSet::iterator::operator==(const WorkingSet::iterator& other) const {
- return (_index == other._index);
-}
-
-bool WorkingSet::iterator::operator!=(const WorkingSet::iterator& other) const {
- return (_index != other._index);
+void WorkingSet::transitionToOwnedObj(WorkingSetID id) {
+ WorkingSetMember* member = get(id);
+ member->transitionToOwnedObj();
}
-WorkingSetMember& WorkingSet::iterator::operator*() {
- dassert(_index < _ws->_data.size() && !isFree());
- return *_ws->_data[_index].member;
-}
-
-WorkingSetMember* WorkingSet::iterator::operator->() {
- dassert(_index < _ws->_data.size() && !isFree());
- return _ws->_data[_index].member;
-}
-
-WorkingSet::iterator WorkingSet::begin() {
- return WorkingSet::iterator(this, 0);
-}
-
-WorkingSet::iterator WorkingSet::end() {
- return WorkingSet::iterator(this, _data.size());
+std::vector<WorkingSetID> WorkingSet::getAndClearYieldSensitiveIds() {
+ std::vector<WorkingSetID> out;
+ // Clear '_yieldSensitiveIds' by swapping it into the set to be returned.
+ _yieldSensitiveIds.swap(out);
+ return out;
}
//
// WorkingSetMember
//
-WorkingSetMember::WorkingSetMember() : state(WorkingSetMember::INVALID), isSuspicious(false) {}
+WorkingSetMember::WorkingSetMember() {}
WorkingSetMember::~WorkingSetMember() {}
@@ -182,23 +148,36 @@ void WorkingSetMember::clear() {
keyData.clear();
obj.reset();
- state = WorkingSetMember::INVALID;
+ _state = WorkingSetMember::INVALID;
}
+WorkingSetMember::MemberState WorkingSetMember::getState() const {
+ return _state;
+}
+
+void WorkingSetMember::transitionToOwnedObj() {
+ invariant(obj.value().isOwned());
+ _state = OWNED_OBJ;
+}
+
+
bool WorkingSetMember::hasLoc() const {
- return state == LOC_AND_IDX || state == LOC_AND_UNOWNED_OBJ || state == LOC_AND_OWNED_OBJ;
+ return _state == LOC_AND_IDX || _state == LOC_AND_OBJ;
}
bool WorkingSetMember::hasObj() const {
- return hasOwnedObj() || hasUnownedObj();
+ return _state == OWNED_OBJ || _state == LOC_AND_OBJ;
}
bool WorkingSetMember::hasOwnedObj() const {
- return state == OWNED_OBJ || state == LOC_AND_OWNED_OBJ;
+ return _state == OWNED_OBJ || (_state == LOC_AND_OBJ && obj.value().isOwned());
}
-bool WorkingSetMember::hasUnownedObj() const {
- return state == LOC_AND_UNOWNED_OBJ;
+void WorkingSetMember::makeObjOwned() {
+ invariant(_state == LOC_AND_OBJ);
+ if (!obj.value().isOwned()) {
+ obj.setValue(obj.value().getOwned());
+ }
}
bool WorkingSetMember::hasComputed(const WorkingSetComputedDataType type) const {
diff --git a/src/mongo/db/exec/working_set.h b/src/mongo/db/exec/working_set.h
index 74080e6f4cc..b8b74de5b96 100644
--- a/src/mongo/db/exec/working_set.h
+++ b/src/mongo/db/exec/working_set.h
@@ -29,6 +29,7 @@
#pragma once
#include <vector>
+#include <unordered_set>
#include "mongo/base/disallow_copying.h"
#include "mongo/db/jsobj.h"
@@ -73,16 +74,23 @@ public:
* Do not delete the returned pointer as the WorkingSet retains ownership. Call free() to
* release it.
*/
- WorkingSetMember* get(const WorkingSetID& i) const {
+ WorkingSetMember* get(WorkingSetID i) const {
dassert(i < _data.size()); // ID has been allocated.
dassert(_data[i].nextFreeOrSelf == i); // ID currently in use.
return _data[i].member;
}
/**
+ * Returns true if WorkingSetMember with id 'i' is free.
+ */
+ bool isFree(WorkingSetID i) const {
+ return _data[i].nextFreeOrSelf != i;
+ }
+
+ /**
* Deallocate the i-th query result and release its resources.
*/
- void free(const WorkingSetID& i);
+ void free(WorkingSetID i);
/**
* The RecordId in WSM 'i' was invalidated while being processed. Any predicates over the
@@ -92,7 +100,7 @@ public:
*
* The WSM must be in the state OWNED_OBJ.
*/
- void flagForReview(const WorkingSetID& i);
+ void flagForReview(WorkingSetID i);
/**
* Return true if the provided ID is flagged.
@@ -110,56 +118,26 @@ public:
void clear();
//
- // Iteration
+ // WorkingSetMember state transitions
//
+ void transitionToLocAndIdx(WorkingSetID id);
+ void transitionToLocAndObj(WorkingSetID id);
+ void transitionToOwnedObj(WorkingSetID id);
+
/**
- * Forward iterates over the list of working set members, skipping any entries
- * that are on the free list.
+ * Returns the list of working set ids that have transitioned into the LOC_AND_IDX or
+ * LOC_AND_OBJ state since the last yield. The members corresponding to these ids may have since
+ * transitioned to a different state or been freed, so these cases must be handled by the
+ * caller. The list may also contain duplicates.
+ *
+ * Execution stages are *not* responsible for managing this list, as working set ids are added
+ * to the set automatically by WorkingSet::transitionToLocAndIdx() and
+ * WorkingSet::transitionToLocAndObj().
+ *
+ * As a side effect, calling this method clears the list of flagged ids kept by the working set.
*/
- class iterator {
- public:
- iterator(WorkingSet* ws, size_t index);
-
- void operator++();
-
- bool operator==(const WorkingSet::iterator& other) const;
- bool operator!=(const WorkingSet::iterator& other) const;
-
- WorkingSetMember& operator*();
-
- WorkingSetMember* operator->();
-
- /**
- * Free the WSM we are currently pointing to. Does not advance the iterator.
- *
- * It is invalid to dereference the iterator after calling free until the iterator is
- * next incremented.
- */
- void free();
-
- private:
- /**
- * Move the iterator forward to the next allocated WSM.
- */
- void advance();
-
- /**
- * Returns true if the MemberHolder currently pointed at by the iterator is free, and
- * false if it contains an allocated working set member.
- */
- bool isFree() const;
-
- // The working set we're iterating over. Not owned here.
- WorkingSet* _ws;
-
- // The index of the member we're currently pointing at.
- size_t _index;
- };
-
- WorkingSet::iterator begin();
-
- WorkingSet::iterator end();
+ std::vector<WorkingSetID> getAndClearYieldSensitiveIds();
private:
struct MemberHolder {
@@ -183,7 +161,10 @@ private:
WorkingSetID _freeList;
// An insert-only set of WorkingSetIDs that have been flagged for review.
- unordered_set<WorkingSetID> _flagged;
+ std::unordered_set<WorkingSetID> _flagged;
+
+ // Contains ids of WSMs that may need to be adjusted when we next yield.
+ std::vector<WorkingSetID> _yieldSensitiveIds;
};
/**
@@ -250,7 +231,7 @@ private:
*
* Index scan stages return a WorkingSetMember in the LOC_AND_IDX state.
*
- * Collection scan stages the LOC_AND_UNOWNED_OBJ state.
+ * Collection scan stages return a WorkingSetMember in the LOC_AND_OBJ state.
*
* A WorkingSetMember may have any of the data above.
*/
@@ -273,38 +254,44 @@ public:
// Data is from 1 or more indices.
LOC_AND_IDX,
- // Data is from a collection scan, or data is from an index scan and was fetched.
- LOC_AND_UNOWNED_OBJ,
+ // Data is from a collection scan, or data is from an index scan and was fetched. The
+ // BSONObj might be owned or unowned.
+ LOC_AND_OBJ,
// RecordId has been invalidated, or the obj doesn't correspond to an on-disk document
// anymore (e.g. is a computed expression).
OWNED_OBJ,
-
- // Due to a yield, RecordId is no longer protected by the storage engine's transaction
- // and may have been invalidated. The object is either identical to the object keyed
- // by RecordId, or is an old version of the document stored at RecordId.
- //
- // Only used by doc-level locking storage engines (not used by MMAP v1).
- LOC_AND_OWNED_OBJ,
};
//
+ // Member state and state transitions
+ //
+
+ MemberState getState() const;
+
+ void transitionToOwnedObj();
+
+ //
// Core attributes
//
RecordId loc;
Snapshotted<BSONObj> obj;
std::vector<IndexKeyDatum> keyData;
- MemberState state;
// True if this WSM has survived a yield in LOC_AND_IDX state.
// TODO consider replacing by tracking SnapshotIds for IndexKeyDatums.
- bool isSuspicious;
+ bool isSuspicious = false;
bool hasLoc() const;
bool hasObj() const;
bool hasOwnedObj() const;
- bool hasUnownedObj() const;
+
+ /**
+ * Ensures that 'obj' is owned BSON. Only valid to call on a working set member in LOC_AND_OBJ
+ * state. No-op if 'obj' is already owned.
+ */
+ void makeObjOwned();
//
// Computed data
@@ -340,6 +327,10 @@ public:
size_t getMemUsage() const;
private:
+ friend class WorkingSet;
+
+ MemberState _state = WorkingSetMember::INVALID;
+
std::unique_ptr<WorkingSetComputedData> _computed[WSM_COMPUTED_NUM_TYPES];
std::unique_ptr<RecordFetcher> _fetcher;
diff --git a/src/mongo/db/exec/working_set_common.cpp b/src/mongo/db/exec/working_set_common.cpp
index 7fb10dd1792..0b79447c506 100644
--- a/src/mongo/db/exec/working_set_common.cpp
+++ b/src/mongo/db/exec/working_set_common.cpp
@@ -44,7 +44,7 @@ bool WorkingSetCommon::fetchAndInvalidateLoc(OperationContext* txn,
WorkingSetMember* member,
const Collection* collection) {
// Already in our desired state.
- if (member->state == WorkingSetMember::OWNED_OBJ) {
+ if (member->getState() == WorkingSetMember::OWNED_OBJ) {
return true;
}
@@ -56,30 +56,39 @@ bool WorkingSetCommon::fetchAndInvalidateLoc(OperationContext* txn,
// Do the fetch, invalidate the DL.
member->obj = collection->docFor(txn, member->loc);
member->obj.setValue(member->obj.value().getOwned());
-
- member->state = WorkingSetMember::OWNED_OBJ;
member->loc = RecordId();
+ member->transitionToOwnedObj();
+
return true;
}
void WorkingSetCommon::prepareForSnapshotChange(WorkingSet* workingSet) {
dassert(supportsDocLocking());
- for (WorkingSet::iterator it = workingSet->begin(); it != workingSet->end(); ++it) {
- if (it->state == WorkingSetMember::LOC_AND_IDX) {
- it->isSuspicious = true;
- } else if (it->state == WorkingSetMember::LOC_AND_UNOWNED_OBJ) {
- // We already have the data so convert directly to owned state.
- it->obj.setValue(it->obj.value().getOwned());
- it->state = WorkingSetMember::LOC_AND_OWNED_OBJ;
+ for (auto id : workingSet->getAndClearYieldSensitiveIds()) {
+ if (workingSet->isFree(id)) {
+ continue;
+ }
+
+ // We may see the same member twice, so anything we do here should be idempotent.
+ WorkingSetMember* member = workingSet->get(id);
+ if (member->getState() == WorkingSetMember::LOC_AND_IDX) {
+ member->isSuspicious = true;
+ } else if (member->getState() == WorkingSetMember::LOC_AND_OBJ) {
+ // Need to make sure that the data is owned, as underlying storage can change during a
+ // yield.
+ member->makeObjOwned();
}
}
}
// static
bool WorkingSetCommon::fetch(OperationContext* txn,
- WorkingSetMember* member,
+ WorkingSet* workingSet,
+ WorkingSetID id,
unowned_ptr<RecordCursor> cursor) {
+ WorkingSetMember* member = workingSet->get(id);
+
// The RecordFetcher should already have been transferred out of the WSM and used.
invariant(!member->hasFetcher());
@@ -114,27 +123,11 @@ bool WorkingSetCommon::fetch(OperationContext* txn,
}
member->keyData.clear();
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ workingSet->transitionToLocAndObj(id);
return true;
}
// static
-void WorkingSetCommon::initFrom(WorkingSetMember* dest, const WorkingSetMember& src) {
- dest->loc = src.loc;
- dest->obj = src.obj;
- dest->keyData = src.keyData;
- dest->state = src.state;
-
- // Merge computed data.
- typedef WorkingSetComputedDataType WSCD;
- for (WSCD i = WSCD(0); i < WSM_COMPUTED_NUM_TYPES; i = WSCD(i + 1)) {
- if (src.hasComputed(i)) {
- dest->addComputed(src.getComputed(i)->clone());
- }
- }
-}
-
-// static
BSONObj WorkingSetCommon::buildMemberStatusObject(const Status& status) {
BSONObjBuilder bob;
bob.append("ok", status.isOK() ? 1.0 : 0.0);
@@ -150,8 +143,8 @@ WorkingSetID WorkingSetCommon::allocateStatusMember(WorkingSet* ws, const Status
WorkingSetID wsid = ws->allocate();
WorkingSetMember* member = ws->get(wsid);
- member->state = WorkingSetMember::OWNED_OBJ;
member->obj = Snapshotted<BSONObj>(SnapshotId(), buildMemberStatusObject(status));
+ member->transitionToOwnedObj();
return wsid;
}
diff --git a/src/mongo/db/exec/working_set_common.h b/src/mongo/db/exec/working_set_common.h
index 2fde57fda3a..6c8efbbb66f 100644
--- a/src/mongo/db/exec/working_set_common.h
+++ b/src/mongo/db/exec/working_set_common.h
@@ -50,42 +50,44 @@ public:
const Collection* collection);
/**
- * This must be called as part of "saveState" operations after all nodes in the tree save
- * their state.
+ * This must be called as part of "saveState" operations after all nodes in the tree save their
+ * state.
*
- * Iterates over 'workingSet' and converts all LOC_AND_UNOWNED_OBJ members to
- * LOC_AND_OWNED_OBJ by calling getOwned on their obj. Also sets the isSuspicious flag on
- * all nodes in LOC_AND_IDX state.
+ * Iterates over WorkingSetIDs in 'workingSet' which are "sensitive to yield". These are ids
+ * that have transitioned into the LOC_AND_IDX or LOC_AND_OBJ state since the previous yield.
+ *
+ * The LOC_AND_IDX members are tagged as suspicious so that they can be handled properly in case
+ * the document keyed by the index key is deleted or updated during the yield. LOC_AND_OBJ
+ * working set members with unowned BSON documents have their 'obj' field made owned in order to
+ * ensure that they don't point into storage which may be modified during yield.
*/
static void prepareForSnapshotChange(WorkingSet* workingSet);
/**
- * Retrieves the document corresponding to 'member' from 'collection', and sets the state of
- * 'member' appropriately.
+ * Transitions the WorkingSetMember with WorkingSetID 'id' from the LOC_AND_IDX state to the
+ * LOC_AND_OBJ state by fetching a document. Does the fetch using RecordCursor 'cursor'.
*
* If false is returned, the document should not be considered for the result set. It is the
- * caller's responsibility to free 'member' in this case.
+ * caller's responsibility to free 'id' in this case.
*
* WriteConflict exceptions may be thrown. When they are, 'member' will be unmodified.
*/
static bool fetch(OperationContext* txn,
- WorkingSetMember* member,
+ WorkingSet* workingSet,
+ WorkingSetID id,
unowned_ptr<RecordCursor> cursor);
static bool fetchIfUnfetched(OperationContext* txn,
- WorkingSetMember* member,
+ WorkingSet* workingSet,
+ WorkingSetID id,
unowned_ptr<RecordCursor> cursor) {
+ WorkingSetMember* member = workingSet->get(id);
if (member->hasObj())
return true;
- return fetch(txn, member, cursor);
+ return fetch(txn, workingSet, id, cursor);
}
/**
- * Initialize the fields in 'dest' from 'src', creating copies of owned objects as needed.
- */
- static void initFrom(WorkingSetMember* dest, const WorkingSetMember& src);
-
- /**
* Build a BSONObj which represents a Status to return in a WorkingSet.
*/
static BSONObj buildMemberStatusObject(const Status& status);
diff --git a/src/mongo/db/exec/working_set_test.cpp b/src/mongo/db/exec/working_set_test.cpp
index ff95717963c..2eeb850bf21 100644
--- a/src/mongo/db/exec/working_set_test.cpp
+++ b/src/mongo/db/exec/working_set_test.cpp
@@ -34,6 +34,7 @@
#include "mongo/db/exec/working_set.h"
#include "mongo/db/json.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/storage/snapshot.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/assert_util.h"
@@ -47,7 +48,7 @@ class WorkingSetFixture : public mongo::unittest::Test {
protected:
void setUp() {
ws.reset(new WorkingSet());
- WorkingSetID id = ws->allocate();
+ id = ws->allocate();
ASSERT(id != WorkingSet::INVALID_ID);
member = ws->get(id);
ASSERT(NULL != member);
@@ -59,6 +60,7 @@ protected:
}
std::unique_ptr<WorkingSet> ws;
+ WorkingSetID id;
WorkingSetMember* member;
};
@@ -66,18 +68,22 @@ TEST_F(WorkingSetFixture, noFieldToGet) {
BSONElement elt;
// Make sure we're not getting anything out of an invalid WSM.
- ASSERT_EQUALS(WorkingSetMember::INVALID, member->state);
+ ASSERT_EQUALS(WorkingSetMember::INVALID, member->getState());
ASSERT_FALSE(member->getFieldDotted("foo", &elt));
- member->state = WorkingSetMember::LOC_AND_IDX;
+ ws->transitionToLocAndIdx(id);
ASSERT_FALSE(member->getFieldDotted("foo", &elt));
// Our state is that of a valid object. The getFieldDotted shouldn't throw; there's
// something to call getFieldDotted on, but there's no field there.
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ ws->transitionToLocAndObj(id);
ASSERT_TRUE(member->getFieldDotted("foo", &elt));
- member->state = WorkingSetMember::OWNED_OBJ;
+ WorkingSetMember* member = ws->get(id);
+ member->obj = {SnapshotId(),
+ BSON("fake"
+ << "obj")};
+ ws->transitionToOwnedObj(id);
ASSERT_TRUE(member->getFieldDotted("foo", &elt));
}
@@ -86,7 +92,7 @@ TEST_F(WorkingSetFixture, getFieldUnowned) {
BSONObj obj = BSON(fieldName << 5);
// Not truthful since the loc is bogus, but the loc isn't accessed anyway...
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ ws->transitionToLocAndObj(id);
member->obj = Snapshotted<BSONObj>(SnapshotId(), BSONObj(obj.objdata()));
ASSERT_TRUE(obj.isOwned());
ASSERT_FALSE(member->obj.value().isOwned());
@@ -103,7 +109,7 @@ TEST_F(WorkingSetFixture, getFieldOwned) {
BSONObj obj = BSON(fieldName << 5);
member->obj = Snapshotted<BSONObj>(SnapshotId(), obj);
ASSERT_TRUE(member->obj.value().isOwned());
- member->state = WorkingSetMember::OWNED_OBJ;
+ ws->transitionToOwnedObj(id);
BSONElement elt;
ASSERT_TRUE(member->getFieldDotted(fieldName, &elt));
ASSERT_EQUALS(elt.numberInt(), 5);
@@ -118,7 +124,7 @@ TEST_F(WorkingSetFixture, getFieldFromIndex) {
member->keyData.push_back(IndexKeyDatum(BSON(firstName << 1), BSON("" << firstValue), NULL));
// Also a minor lie as loc is bogus.
- member->state = WorkingSetMember::LOC_AND_IDX;
+ ws->transitionToLocAndIdx(id);
BSONElement elt;
ASSERT_TRUE(member->getFieldDotted(firstName, &elt));
ASSERT_EQUALS(elt.numberInt(), firstValue);
@@ -140,7 +146,7 @@ TEST_F(WorkingSetFixture, getDottedFieldFromIndex) {
int firstValue = 5;
member->keyData.push_back(IndexKeyDatum(BSON(firstName << 1), BSON("" << firstValue), NULL));
- member->state = WorkingSetMember::LOC_AND_IDX;
+ ws->transitionToLocAndIdx(id);
BSONElement elt;
ASSERT_TRUE(member->getFieldDotted(firstName, &elt));
ASSERT_EQUALS(elt.numberInt(), firstValue);
@@ -148,100 +154,4 @@ TEST_F(WorkingSetFixture, getDottedFieldFromIndex) {
ASSERT_FALSE(member->getFieldDotted("y", &elt));
}
-//
-// WorkingSet::iterator tests
-//
-
-TEST(WorkingSetIteratorTest, BasicIteratorTest) {
- WorkingSet ws;
-
- WorkingSetID id1 = ws.allocate();
- WorkingSetMember* member1 = ws.get(id1);
- member1->state = WorkingSetMember::LOC_AND_IDX;
- member1->keyData.push_back(IndexKeyDatum(BSON("a" << 1), BSON("" << 3), NULL));
-
- WorkingSetID id2 = ws.allocate();
- WorkingSetMember* member2 = ws.get(id2);
- member2->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- member2->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("a" << 3));
-
- int counter = 0;
- for (WorkingSet::iterator it = ws.begin(); it != ws.end(); ++it) {
- ASSERT(it->state == WorkingSetMember::LOC_AND_IDX ||
- it->state == WorkingSetMember::LOC_AND_UNOWNED_OBJ);
- counter++;
- }
- ASSERT_EQ(counter, 2);
-}
-
-TEST(WorkingSetIteratorTest, EmptyWorkingSet) {
- WorkingSet ws;
-
- int counter = 0;
- for (WorkingSet::iterator it = ws.begin(); it != ws.end(); ++it) {
- counter++;
- }
- ASSERT_EQ(counter, 0);
-}
-
-TEST(WorkingSetIteratorTest, EmptyWorkingSetDueToFree) {
- WorkingSet ws;
-
- WorkingSetID id = ws.allocate();
- ws.free(id);
-
- int counter = 0;
- for (WorkingSet::iterator it = ws.begin(); it != ws.end(); ++it) {
- counter++;
- }
- ASSERT_EQ(counter, 0);
-}
-
-TEST(WorkingSetIteratorTest, MixedFreeAndInUse) {
- WorkingSet ws;
-
- WorkingSetID id1 = ws.allocate();
- WorkingSetID id2 = ws.allocate();
- WorkingSetID id3 = ws.allocate();
-
- WorkingSetMember* member = ws.get(id2);
- member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- member->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("a" << 3));
-
- ws.free(id1);
- ws.free(id3);
-
- int counter = 0;
- for (WorkingSet::iterator it = ws.begin(); it != ws.end(); ++it) {
- ASSERT(it->state == WorkingSetMember::LOC_AND_UNOWNED_OBJ);
- counter++;
- }
- ASSERT_EQ(counter, 1);
-}
-
-TEST(WorkingSetIteratorTest, FreeWhileIterating) {
- WorkingSet ws;
-
- ws.allocate();
- ws.allocate();
- ws.allocate();
-
- // Free the last two members during iteration.
- int counter = 0;
- for (WorkingSet::iterator it = ws.begin(); it != ws.end(); ++it) {
- if (counter > 0) {
- it.free();
- }
- counter++;
- }
- ASSERT_EQ(counter, 3);
-
- // Verify that only one item remains in the working set.
- counter = 0;
- for (WorkingSet::iterator it = ws.begin(); it != ws.end(); ++it) {
- counter++;
- }
- ASSERT_EQ(counter, 1);
-}
-
} // namespace
diff --git a/src/mongo/db/query/plan_executor.cpp b/src/mongo/db/query/plan_executor.cpp
index 26edbc2ae60..3c216ba313e 100644
--- a/src/mongo/db/query/plan_executor.cpp
+++ b/src/mongo/db/query/plan_executor.cpp
@@ -383,7 +383,7 @@ PlanExecutor::ExecState PlanExecutor::getNextSnapshotted(Snapshotted<BSONObj>* o
bool hasRequestedData = true;
if (NULL != objOut) {
- if (WorkingSetMember::LOC_AND_IDX == member->state) {
+ if (WorkingSetMember::LOC_AND_IDX == member->getState()) {
if (1 != member->keyData.size()) {
_workingSet->free(id);
hasRequestedData = false;
diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h
index 9cef086ff65..54c63751989 100644
--- a/src/mongo/db/query/query_solution.h
+++ b/src/mongo/db/query/query_solution.h
@@ -227,8 +227,7 @@ struct TextNode : public QuerySolutionNode {
virtual void appendToString(mongoutils::str::stream* ss, int indent) const;
- // Text's return is LOC_AND_UNOWNED_OBJ or LOC_AND_OWNED_OBJ so it's fetched and has all
- // fields.
+ // Text's return is LOC_AND_OBJ so it's fetched and has all fields.
bool fetched() const {
return true;
}
diff --git a/src/mongo/dbtests/query_stage_and.cpp b/src/mongo/dbtests/query_stage_and.cpp
index f34a84eec06..f1af8d32704 100644
--- a/src/mongo/dbtests/query_stage_and.cpp
+++ b/src/mongo/dbtests/query_stage_and.cpp
@@ -235,7 +235,7 @@ public:
// Expect to find the right value of foo in the flagged item.
WorkingSetMember* member = ws.get(*flagged.begin());
ASSERT_TRUE(NULL != member);
- ASSERT_EQUALS(WorkingSetMember::OWNED_OBJ, member->state);
+ ASSERT_EQUALS(WorkingSetMember::OWNED_OBJ, member->getState());
BSONElement elt;
ASSERT_TRUE(member->getFieldDotted("foo", &elt));
ASSERT_EQUALS(15, elt.numberInt());
@@ -885,11 +885,12 @@ public:
std::unique_ptr<QueuedDataStage> childStage1 = stdx::make_unique<QueuedDataStage>(&ws);
{
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- wsm.loc = RecordId(1);
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
- childStage1->pushBack(wsm);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* wsm = ws.get(id);
+ wsm->loc = RecordId(1);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
+ ws.transitionToLocAndObj(id);
+ childStage1->pushBack(id);
}
std::unique_ptr<QueuedDataStage> childStage2 = stdx::make_unique<QueuedDataStage>(&ws);
@@ -919,21 +920,23 @@ public:
std::unique_ptr<QueuedDataStage> childStage1 = stdx::make_unique<QueuedDataStage>(&ws);
{
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- wsm.loc = RecordId(1);
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
- childStage1->pushBack(wsm);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* wsm = ws.get(id);
+ wsm->loc = RecordId(1);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
+ ws.transitionToLocAndObj(id);
+ childStage1->pushBack(id);
}
childStage1->pushBack(PlanStage::DEAD);
std::unique_ptr<QueuedDataStage> childStage2 = stdx::make_unique<QueuedDataStage>(&ws);
{
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- wsm.loc = RecordId(2);
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
- childStage2->pushBack(wsm);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* wsm = ws.get(id);
+ wsm->loc = RecordId(2);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
+ ws.transitionToLocAndObj(id);
+ childStage2->pushBack(id);
}
andHashStage->addChild(childStage1.release());
@@ -958,20 +961,22 @@ public:
std::unique_ptr<QueuedDataStage> childStage1 = stdx::make_unique<QueuedDataStage>(&ws);
{
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- wsm.loc = RecordId(1);
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
- childStage1->pushBack(wsm);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* wsm = ws.get(id);
+ wsm->loc = RecordId(1);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
+ ws.transitionToLocAndObj(id);
+ childStage1->pushBack(id);
}
std::unique_ptr<QueuedDataStage> childStage2 = stdx::make_unique<QueuedDataStage>(&ws);
{
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- wsm.loc = RecordId(2);
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
- childStage2->pushBack(wsm);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* wsm = ws.get(id);
+ wsm->loc = RecordId(2);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), dataObj);
+ ws.transitionToLocAndObj(id);
+ childStage2->pushBack(id);
}
childStage2->pushBack(PlanStage::DEAD);
@@ -1056,7 +1061,7 @@ public:
// Make sure the nuked obj is actually in the flagged data.
ASSERT_EQUALS(ws.getFlagged().size(), size_t(1));
WorkingSetMember* member = ws.get(*ws.getFlagged().begin());
- ASSERT_EQUALS(WorkingSetMember::OWNED_OBJ, member->state);
+ ASSERT_EQUALS(WorkingSetMember::OWNED_OBJ, member->getState());
BSONElement elt;
ASSERT_TRUE(member->getFieldDotted("foo", &elt));
ASSERT_EQUALS(1, elt.numberInt());
diff --git a/src/mongo/dbtests/query_stage_delete.cpp b/src/mongo/dbtests/query_stage_delete.cpp
index e3d3ace2054..5c7e888e552 100644
--- a/src/mongo/dbtests/query_stage_delete.cpp
+++ b/src/mongo/dbtests/query_stage_delete.cpp
@@ -202,14 +202,15 @@ public:
getLocs(coll, CollectionScanParams::FORWARD, &locs);
// Configure a QueuedDataStage to pass the first object in the collection back in a
- // LOC_AND_UNOWNED_OBJ state.
- unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
- WorkingSetMember member;
- member.loc = locs[targetDocIndex];
- member.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ // LOC_AND_OBJ state.
+ std::unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->loc = locs[targetDocIndex];
const BSONObj oldDoc = BSON("_id" << targetDocIndex << "foo" << targetDocIndex);
- member.obj = Snapshotted<BSONObj>(SnapshotId(), oldDoc);
- qds->pushBack(member);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), oldDoc);
+ ws->transitionToLocAndObj(id);
+ qds->pushBack(id);
// Configure the delete.
DeleteStageParams deleteParams;
@@ -222,7 +223,7 @@ public:
const DeleteStats* stats = static_cast<const DeleteStats*>(deleteStage->getSpecificStats());
// Should return advanced.
- WorkingSetID id = WorkingSet::INVALID_ID;
+ id = WorkingSet::INVALID_ID;
PlanStage::StageState state = deleteStage->work(&id);
ASSERT_EQUALS(PlanStage::ADVANCED, state);
@@ -234,7 +235,7 @@ public:
// With an owned copy of the object, with no RecordId.
ASSERT_TRUE(resultMember->hasOwnedObj());
ASSERT_FALSE(resultMember->hasLoc());
- ASSERT_EQUALS(resultMember->state, WorkingSetMember::OWNED_OBJ);
+ ASSERT_EQUALS(resultMember->getState(), WorkingSetMember::OWNED_OBJ);
ASSERT_TRUE(resultMember->obj.value().isOwned());
// Should be the old value.
@@ -264,10 +265,13 @@ public:
// Configure a QueuedDataStage to pass an OWNED_OBJ to the delete stage.
unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
- WorkingSetMember member;
- member.state = WorkingSetMember::OWNED_OBJ;
- member.obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{x: 1}"));
- qds->pushBack(member);
+ {
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{x: 1}"));
+ member->transitionToOwnedObj();
+ qds->pushBack(id);
+ }
// Configure the delete.
DeleteStageParams deleteParams;
diff --git a/src/mongo/dbtests/query_stage_fetch.cpp b/src/mongo/dbtests/query_stage_fetch.cpp
index fd79ad50baa..3b7443faaf7 100644
--- a/src/mongo/dbtests/query_stage_fetch.cpp
+++ b/src/mongo/dbtests/query_stage_fetch.cpp
@@ -111,18 +111,22 @@ public:
// Mock data.
{
- WorkingSetMember mockMember;
- mockMember.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- mockMember.loc = *locs.begin();
- mockMember.obj = coll->docFor(&_txn, mockMember.loc);
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* mockMember = ws.get(id);
+ mockMember->loc = *locs.begin();
+ mockMember->obj = coll->docFor(&_txn, mockMember->loc);
+ ws.transitionToLocAndObj(id);
// Points into our DB.
- mockStage->pushBack(mockMember);
-
- mockMember.state = WorkingSetMember::OWNED_OBJ;
- mockMember.loc = RecordId();
- mockMember.obj = Snapshotted<BSONObj>(SnapshotId(), BSON("foo" << 6));
- ASSERT_TRUE(mockMember.obj.value().isOwned());
- mockStage->pushBack(mockMember);
+ mockStage->pushBack(id);
+ }
+ {
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* mockMember = ws.get(id);
+ mockMember->loc = RecordId();
+ mockMember->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("foo" << 6));
+ mockMember->transitionToOwnedObj();
+ ASSERT_TRUE(mockMember->obj.value().isOwned());
+ mockStage->pushBack(id);
}
unique_ptr<FetchStage> fetchStage(
@@ -173,14 +177,15 @@ public:
// Mock data.
{
- WorkingSetMember mockMember;
- mockMember.state = WorkingSetMember::LOC_AND_IDX;
- mockMember.loc = *locs.begin();
+ WorkingSetID id = ws.allocate();
+ WorkingSetMember* mockMember = ws.get(id);
+ mockMember->loc = *locs.begin();
+ ws.transitionToLocAndIdx(id);
// State is loc and index, shouldn't be able to get the foo data inside.
BSONElement elt;
- ASSERT_FALSE(mockMember.getFieldDotted("foo", &elt));
- mockStage->pushBack(mockMember);
+ ASSERT_FALSE(mockMember->getFieldDotted("foo", &elt));
+ mockStage->pushBack(id);
}
// Make the filter.
diff --git a/src/mongo/dbtests/query_stage_ixscan.cpp b/src/mongo/dbtests/query_stage_ixscan.cpp
index b63c1db2a1c..d6fcaf177d4 100644
--- a/src/mongo/dbtests/query_stage_ixscan.cpp
+++ b/src/mongo/dbtests/query_stage_ixscan.cpp
@@ -185,10 +185,10 @@ public:
// Expect to get key {'': 5} and then key {'': 6}.
WorkingSetMember* member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 5));
member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 6));
// Save state and insert a few indexed docs.
@@ -198,7 +198,7 @@ public:
ixscan->restoreState(&_txn);
member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 10));
WorkingSetID id;
@@ -222,7 +222,7 @@ public:
// Expect to get key {'': 6}.
WorkingSetMember* member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 6));
// Save state and insert an indexed doc.
@@ -231,7 +231,7 @@ public:
ixscan->restoreState(&_txn);
member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 7));
WorkingSetID id;
@@ -255,7 +255,7 @@ public:
// Expect to get key {'': 6}.
WorkingSetMember* member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 6));
// Save state and insert an indexed doc.
@@ -285,10 +285,10 @@ public:
// Expect to get key {'': 10} and then {'': 8}.
WorkingSetMember* member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 10));
member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 8));
// Save state and insert an indexed doc.
@@ -299,7 +299,7 @@ public:
// Ensure that we don't erroneously return {'': 9} or {'':3}.
member = getNext(ixscan.get());
- ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->state);
+ ASSERT_EQ(WorkingSetMember::LOC_AND_IDX, member->getState());
ASSERT_EQ(member->keyData[0].keyData, BSON("" << 6));
WorkingSetID id;
diff --git a/src/mongo/dbtests/query_stage_keep.cpp b/src/mongo/dbtests/query_stage_keep.cpp
index 221272d9b1b..9d1e0e13789 100644
--- a/src/mongo/dbtests/query_stage_keep.cpp
+++ b/src/mongo/dbtests/query_stage_keep.cpp
@@ -126,8 +126,8 @@ public:
for (size_t i = 0; i < 10; ++i) {
WorkingSetID id = ws.allocate();
WorkingSetMember* member = ws.get(id);
- member->state = WorkingSetMember::OWNED_OBJ;
member->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << 2));
+ member->transitionToOwnedObj();
ws.flagForReview(id);
}
@@ -195,8 +195,8 @@ public:
for (size_t i = 0; i < 50; ++i) {
WorkingSetID id = ws.allocate();
WorkingSetMember* member = ws.get(id);
- member->state = WorkingSetMember::OWNED_OBJ;
member->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << 1));
+ member->transitionToOwnedObj();
ws.flagForReview(id);
expectedResultIds.insert(id);
}
@@ -220,8 +220,8 @@ public:
while (ws.getFlagged().size() <= rehashSize) {
WorkingSetID id = ws.allocate();
WorkingSetMember* member = ws.get(id);
- member->state = WorkingSetMember::OWNED_OBJ;
member->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << 1));
+ member->transitionToOwnedObj();
ws.flagForReview(id);
}
while ((id = getNextResult(keep.get())) != WorkingSet::INVALID_ID) {
diff --git a/src/mongo/dbtests/query_stage_limit_skip.cpp b/src/mongo/dbtests/query_stage_limit_skip.cpp
index 8ed129a8f95..31b6cda5241 100644
--- a/src/mongo/dbtests/query_stage_limit_skip.cpp
+++ b/src/mongo/dbtests/query_stage_limit_skip.cpp
@@ -57,10 +57,13 @@ QueuedDataStage* getMS(WorkingSet* ws) {
// Put N ADVANCED results into the mock stage, and some other stalling results (YIELD/TIME).
for (int i = 0; i < N; ++i) {
ms->pushBack(PlanStage::NEED_TIME);
- WorkingSetMember wsm;
- wsm.state = WorkingSetMember::OWNED_OBJ;
- wsm.obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << i));
- ms->pushBack(wsm);
+
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* wsm = ws->get(id);
+ wsm->obj = Snapshotted<BSONObj>(SnapshotId(), BSON("x" << i));
+ wsm->transitionToOwnedObj();
+ ms->pushBack(id);
+
ms->pushBack(PlanStage::NEED_TIME);
}
diff --git a/src/mongo/dbtests/query_stage_near.cpp b/src/mongo/dbtests/query_stage_near.cpp
index f007cc6af9b..f7694d19018 100644
--- a/src/mongo/dbtests/query_stage_near.cpp
+++ b/src/mongo/dbtests/query_stage_near.cpp
@@ -72,8 +72,8 @@ public:
*out = _workingSet->allocate();
WorkingSetMember* member = _workingSet->get(*out);
- member->state = WorkingSetMember::OWNED_OBJ;
member->obj = Snapshotted<BSONObj>(SnapshotId(), next);
+ member->transitionToOwnedObj();
return PlanStage::ADVANCED;
}
diff --git a/src/mongo/dbtests/query_stage_sort.cpp b/src/mongo/dbtests/query_stage_sort.cpp
index 0de6c1a3d9e..b49ccc9dec8 100644
--- a/src/mongo/dbtests/query_stage_sort.cpp
+++ b/src/mongo/dbtests/query_stage_sort.cpp
@@ -79,7 +79,7 @@ public:
/**
* We feed a mix of (key, unowned, owned) data to the sort stage.
*/
- void insertVarietyOfObjects(QueuedDataStage* ms, Collection* coll) {
+ void insertVarietyOfObjects(WorkingSet* ws, QueuedDataStage* ms, Collection* coll) {
set<RecordId> locs;
getLocs(&locs, coll);
@@ -89,11 +89,12 @@ public:
ASSERT_FALSE(it == locs.end());
// Insert some owned obj data.
- WorkingSetMember member;
- member.loc = *it;
- member.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
- member.obj = coll->docFor(&_txn, *it);
- ms->pushBack(member);
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->loc = *it;
+ member->obj = coll->docFor(&_txn, *it);
+ ws->transitionToLocAndObj(id);
+ ms->pushBack(id);
}
}
@@ -105,7 +106,7 @@ public:
// Build the mock scan stage which feeds the data.
unique_ptr<WorkingSet> ws(new WorkingSet());
unique_ptr<QueuedDataStage> ms(new QueuedDataStage(ws.get()));
- insertVarietyOfObjects(ms.get(), coll);
+ insertVarietyOfObjects(ws.get(), ms.get(), coll);
SortStageParams params;
params.collection = coll;
@@ -140,7 +141,7 @@ public:
QueuedDataStage* ms = new QueuedDataStage(ws.get());
// Insert a mix of the various types of data.
- insertVarietyOfObjects(ms, coll);
+ insertVarietyOfObjects(ws.get(), ms, coll);
SortStageParams params;
params.collection = coll;
@@ -516,15 +517,21 @@ public:
QueuedDataStage* ms = new QueuedDataStage(ws.get());
for (int i = 0; i < numObj(); ++i) {
- WorkingSetMember member;
- member.state = WorkingSetMember::OWNED_OBJ;
-
- member.obj = Snapshotted<BSONObj>(
- SnapshotId(), fromjson("{a: [1,2,3], b:[1,2,3], c:[1,2,3], d:[1,2,3,4]}"));
- ms->pushBack(member);
-
- member.obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{a:1, b:1, c:1}"));
- ms->pushBack(member);
+ {
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->obj = Snapshotted<BSONObj>(
+ SnapshotId(), fromjson("{a: [1,2,3], b:[1,2,3], c:[1,2,3], d:[1,2,3,4]}"));
+ member->transitionToOwnedObj();
+ ms->pushBack(id);
+ }
+ {
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{a:1, b:1, c:1}"));
+ member->transitionToOwnedObj();
+ ms->pushBack(id);
+ }
}
SortStageParams params;
diff --git a/src/mongo/dbtests/query_stage_update.cpp b/src/mongo/dbtests/query_stage_update.cpp
index 2e80ef886aa..3290e40ad6c 100644
--- a/src/mongo/dbtests/query_stage_update.cpp
+++ b/src/mongo/dbtests/query_stage_update.cpp
@@ -394,14 +394,15 @@ public:
ASSERT_OK(driver.parse(request.getUpdates(), request.isMulti()));
// Configure a QueuedDataStage to pass the first object in the collection back in a
- // LOC_AND_UNOWNED_OBJ state.
- unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
- WorkingSetMember member;
- member.loc = locs[targetDocIndex];
- member.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ // LOC_AND_OBJ state.
+ std::unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->loc = locs[targetDocIndex];
const BSONObj oldDoc = BSON("_id" << targetDocIndex << "foo" << targetDocIndex);
- member.obj = Snapshotted<BSONObj>(SnapshotId(), oldDoc);
- qds->pushBack(member);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), oldDoc);
+ ws->transitionToLocAndObj(id);
+ qds->pushBack(id);
// Configure the update.
UpdateStageParams updateParams(&request, &driver, opDebug);
@@ -411,7 +412,7 @@ public:
stdx::make_unique<UpdateStage>(&_txn, updateParams, ws.get(), coll, qds.release()));
// Should return advanced.
- WorkingSetID id = WorkingSet::INVALID_ID;
+ id = WorkingSet::INVALID_ID;
PlanStage::StageState state = updateStage->work(&id);
ASSERT_EQUALS(PlanStage::ADVANCED, state);
@@ -423,7 +424,7 @@ public:
// With an owned copy of the object, with no RecordId.
ASSERT_TRUE(resultMember->hasOwnedObj());
ASSERT_FALSE(resultMember->hasLoc());
- ASSERT_EQUALS(resultMember->state, WorkingSetMember::OWNED_OBJ);
+ ASSERT_EQUALS(resultMember->getState(), WorkingSetMember::OWNED_OBJ);
ASSERT_TRUE(resultMember->obj.value().isOwned());
// Should be the old value.
@@ -481,14 +482,15 @@ public:
ASSERT_OK(driver.parse(request.getUpdates(), request.isMulti()));
// Configure a QueuedDataStage to pass the first object in the collection back in a
- // LOC_AND_UNOWNED_OBJ state.
- unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
- WorkingSetMember member;
- member.loc = locs[targetDocIndex];
- member.state = WorkingSetMember::LOC_AND_UNOWNED_OBJ;
+ // LOC_AND_OBJ state.
+ std::unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->loc = locs[targetDocIndex];
const BSONObj oldDoc = BSON("_id" << targetDocIndex << "foo" << targetDocIndex);
- member.obj = Snapshotted<BSONObj>(SnapshotId(), oldDoc);
- qds->pushBack(member);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), oldDoc);
+ ws->transitionToLocAndObj(id);
+ qds->pushBack(id);
// Configure the update.
UpdateStageParams updateParams(&request, &driver, opDebug);
@@ -498,7 +500,7 @@ public:
stdx::make_unique<UpdateStage>(&_txn, updateParams, ws.get(), coll, qds.release()));
// Should return advanced.
- WorkingSetID id = WorkingSet::INVALID_ID;
+ id = WorkingSet::INVALID_ID;
PlanStage::StageState state = updateStage->work(&id);
ASSERT_EQUALS(PlanStage::ADVANCED, state);
@@ -510,7 +512,7 @@ public:
// With an owned copy of the object, with no RecordId.
ASSERT_TRUE(resultMember->hasOwnedObj());
ASSERT_FALSE(resultMember->hasLoc());
- ASSERT_EQUALS(resultMember->state, WorkingSetMember::OWNED_OBJ);
+ ASSERT_EQUALS(resultMember->getState(), WorkingSetMember::OWNED_OBJ);
ASSERT_TRUE(resultMember->obj.value().isOwned());
// Should be the new value.
@@ -558,10 +560,13 @@ public:
// Configure a QueuedDataStage to pass an OWNED_OBJ to the update stage.
unique_ptr<QueuedDataStage> qds(stdx::make_unique<QueuedDataStage>(ws.get()));
- WorkingSetMember member;
- member.state = WorkingSetMember::OWNED_OBJ;
- member.obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{x: 1}"));
- qds->pushBack(member);
+ {
+ WorkingSetID id = ws->allocate();
+ WorkingSetMember* member = ws->get(id);
+ member->obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{x: 1}"));
+ member->transitionToOwnedObj();
+ qds->pushBack(id);
+ }
// Configure the update.
UpdateStageParams updateParams(&request, &driver, opDebug);