PlanStage::StageState FetchStage::fetchCompleted(WorkingSetID* out) { WorkingSetMember* member = _ws->get(_idBeingPagedIn); // The DiskLoc we're waiting to page in was invalidated (forced fetch). Test for // matching and maybe pass it up. if (member->state == WorkingSetMember::OWNED_OBJ) { WorkingSetID memberID = _idBeingPagedIn; _idBeingPagedIn = WorkingSet::INVALID_ID; return returnIfMatches(member, memberID, out); } // Assume that the caller has fetched appropriately. // TODO: Do we want to double-check the runner? Not sure how reliable likelyInMemory is // on all platforms. verify(member->hasLoc()); verify(!member->hasObj()); // Make the (unowned) object. Record* record = member->loc.rec(); const char* data = record->dataNoThrowing(); member->obj = BSONObj(data); // Don't need index data anymore as we have an obj. member->keyData.clear(); member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ; verify(!member->obj.isOwned()); // Return the obj if it passes our filter. WorkingSetID memberID = _idBeingPagedIn; _idBeingPagedIn = WorkingSet::INVALID_ID; return returnIfMatches(member, memberID, out); }
PlanStage::StageState FetchStage::work(WorkingSetID* out) { ++_commonStats.works; if (isEOF()) { return PlanStage::IS_EOF; } // If we asked our parent for a page-in last time work(...) was called, finish the fetch. if (WorkingSet::INVALID_ID != _idBeingPagedIn) { cout << "fetch completed, id being paged on " << _idBeingPagedIn << endl; return fetchCompleted(out); } // If we're here, we're not waiting for a DiskLoc to be fetched. Get another to-be-fetched // result from our child. WorkingSetID id; StageState status = _child->work(&id); if (PlanStage::ADVANCED == status) { WorkingSetMember* member = _ws->get(id); // If there's an obj there, there is no fetching to perform. if (member->hasObj()) { ++_specificStats.alreadyHasObj; return returnIfMatches(member, id, out); } // 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(member->hasLoc()); Record* record = member->loc.rec(); const char* data = record->dataNoThrowing(); if (!recordInMemory(data)) { // member->loc points to a record that's NOT in memory. Pass a fetch request up. verify(WorkingSet::INVALID_ID == _idBeingPagedIn); _idBeingPagedIn = id; *out = id; ++_commonStats.needFetch; return PlanStage::NEED_FETCH; } else { // Don't need index data anymore as we have an obj. member->keyData.clear(); member->obj = BSONObj(data); member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ; return returnIfMatches(member, id, out); } } else { if (PlanStage::NEED_FETCH == status) { *out = id; ++_commonStats.needFetch; } else if (PlanStage::NEED_TIME == status) { ++_commonStats.needTime; } return status; } }
PlanStage::StageState FetchStage::work(WorkingSetID* out) { ++_commonStats.works; // Adds the amount of time taken by work() to executionTimeMillis. ScopedTimer timer(&_commonStats.executionTimeMillis); if (isEOF()) { return PlanStage::IS_EOF; } // If we're here, we're not waiting for a DiskLoc to be fetched. Get another to-be-fetched // result from our child. WorkingSetID id = WorkingSet::INVALID_ID; StageState status = _child->work(&id); if (PlanStage::ADVANCED == status) { WorkingSetMember* member = _ws->get(id); // If there's an obj there, there is no fetching to perform. if (member->hasObj()) { ++_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(member->hasLoc()); // Don't need index data anymore as we have an obj. member->keyData.clear(); member->obj = _collection->docFor(member->loc); member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ; } ++_specificStats.docsExamined; return returnIfMatches(member, id, out); } else if (PlanStage::FAILURE == status) { *out = id; // If a stage fails, it may create a status WSM to indicate why it // failed, in which case 'id' is valid. If ID is invalid, we // create our own error message. if (WorkingSet::INVALID_ID == id) { mongoutils::str::stream ss; ss << "fetch stage failed to read in results from child"; Status status(ErrorCodes::InternalError, ss); *out = WorkingSetCommon::allocateStatusMember( _ws, status); } return status; } else { if (PlanStage::NEED_TIME == status) { ++_commonStats.needTime; } return status; } }
PlanStage::StageState CollectionScan::work(WorkingSetID* out) { ++_commonStats.works; // Adds the amount of time taken by work() to executionTimeMillis. ScopedTimer timer(&_commonStats.executionTimeMillis); if (_isDead) { return PlanStage::DEAD; } // Do some init if we haven't already. if (NULL == _iter) { if ( _params.collection == NULL ) { _isDead = true; return PlanStage::DEAD; } if (_lastSeenLoc.isNull()) { _iter.reset( _params.collection->getIterator( _txn, _params.start, _params.direction ) ); } else { invariant(_params.tailable); _iter.reset( _params.collection->getIterator( _txn, _lastSeenLoc, _params.direction ) ); // Advance _iter past where we were last time. If it returns something else, mark us // as dead since we want to signal an error rather than silently dropping data from // the stream. This is related to the _lastSeenLock handling in invalidate. if (_iter->getNext() != _lastSeenLoc) { _isDead = true; return PlanStage::DEAD; } } ++_commonStats.needTime; return PlanStage::NEED_TIME; } // Should we try getNext() on the underlying _iter? if (isEOF()) return PlanStage::IS_EOF; const DiskLoc curr = _iter->curr(); if (curr.isNull()) { // We just hit EOF if (_params.tailable) _iter.reset(); // pick up where we left off on the next call to work() return PlanStage::IS_EOF; } _lastSeenLoc = curr; // See if the record we're about to access is in memory. If not, pass a fetch request up. // Note that curr() does not touch the record (on MMAPv1 which is the only place we use // NEED_FETCH) so we are able to yield before touching the record, as long as we do so // before calling getNext(). { std::auto_ptr<RecordFetcher> fetcher( _params.collection->documentNeedsFetch(_txn, curr)); if (NULL != fetcher.get()) { WorkingSetMember* member = _workingSet->get(_wsidForFetch); member->loc = curr; // Pass the RecordFetcher off to the WSM. member->setFetcher(fetcher.release()); *out = _wsidForFetch; _commonStats.needFetch++; return NEED_FETCH; } } WorkingSetID id = _workingSet->allocate(); WorkingSetMember* member = _workingSet->get(id); member->loc = curr; member->obj = _iter->dataFor(member->loc).releaseToBson(); member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ; // Advance the iterator. invariant(_iter->getNext() == curr); return returnIfMatches(member, id, out); }
PlanStage::StageState CollectionScan::work(WorkingSetID* out) { ++_commonStats.works; // Adds the amount of time taken by work() to executionTimeMillis. ScopedTimer timer(&_commonStats.executionTimeMillis); if (_isDead) { return PlanStage::DEAD; } // Do some init if we haven't already. if (NULL == _iter) { if ( _params.collection == NULL ) { _isDead = true; return PlanStage::DEAD; } if (_lastSeenLoc.isNull()) { _iter.reset( _params.collection->getIterator( _txn, _params.start, _params.direction ) ); } else { invariant(_params.tailable); _iter.reset( _params.collection->getIterator( _txn, _lastSeenLoc, _params.direction ) ); // Advance _iter past where we were last time. If it returns something else, mark us // as dead since we want to signal an error rather than silently dropping data from // the stream. This is related to the _lastSeenLock handling in invalidate. if (_iter->getNext() != _lastSeenLoc) { _isDead = true; return PlanStage::DEAD; } } ++_commonStats.needTime; return PlanStage::NEED_TIME; } // Should we try getNext() on the underlying _iter? if (isEOF()) return PlanStage::IS_EOF; // See if the record we're about to access is in memory. If not, pass a fetch request up. // Note that curr() returns the same thing as getNext() will, except without advancing the // iterator or touching the DiskLoc. This means that we can use curr() to check whether we // need to fetch on the DiskLoc prior to touching it with getNext(). DiskLoc curr = _iter->curr(); if (!curr.isNull()) { std::auto_ptr<RecordFetcher> fetcher( _params.collection->documentNeedsFetch(_txn, curr)); if (NULL != fetcher.get()) { WorkingSetMember* member = _workingSet->get(_wsidForFetch); member->loc = curr; // Pass the RecordFetcher off to the WSM. member->setFetcher(fetcher.release()); *out = _wsidForFetch; _commonStats.needFetch++; return NEED_FETCH; } } // What we'll return to the user. DiskLoc nextLoc; // See if _iter gives us anything new. nextLoc = _iter->getNext(); if (nextLoc.isNull()) { if (_params.tailable) _iter.reset(); // pick up where we left off on the next call to work() return PlanStage::IS_EOF; } _lastSeenLoc = nextLoc; WorkingSetID id = _workingSet->allocate(); WorkingSetMember* member = _workingSet->get(id); member->loc = nextLoc; member->obj = _iter->dataFor(member->loc).releaseToBson(); member->state = WorkingSetMember::LOC_AND_UNOWNED_OBJ; return returnIfMatches(member, id, out); }