diff options
author | David Storch <david.storch@10gen.com> | 2015-06-22 16:37:08 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2015-07-01 14:42:17 -0400 |
commit | 820289c3c4244666e1829732150d842af775e56d (patch) | |
tree | 9844ae54f597514bd8fb49b9fe04831ae700770f | |
parent | 4f4e36b69bfd6ea8615e2c0786a4d1dbca502a74 (diff) | |
download | mongo-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.
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); |