diff options
author | David Storch <david.storch@10gen.com> | 2018-11-08 16:23:40 -0500 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2018-11-27 13:33:54 -0500 |
commit | a0f64a185b60422fe15b67a406c7b8e04c191937 (patch) | |
tree | 4284490a2d3be19a40c447e2299dac4e0101c4a7 /src/mongo/db | |
parent | 99ec5dabaf1286a5440da0bb561e32d3aa79466a (diff) | |
download | mongo-a0f64a185b60422fe15b67a406c7b8e04c191937.tar.gz |
SERVER-37447 Introduce RequiresIndexStage and use for IXSCAN.
Diffstat (limited to 'src/mongo/db')
33 files changed, 351 insertions, 146 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 2e5bd249acb..8a71699022b 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1064,6 +1064,7 @@ env.Library( 'exec/queued_data_stage.cpp', 'exec/record_store_fast_count.cpp', 'exec/requires_collection_stage.cpp', + 'exec/requires_index_stage.cpp', 'exec/shard_filter.cpp', 'exec/skip.cpp', 'exec/sort.cpp', diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h index eadb7a56733..386a344d65c 100644 --- a/src/mongo/db/catalog/index_catalog.h +++ b/src/mongo/db/catalog/index_catalog.h @@ -280,10 +280,18 @@ public: const IndexDescriptor* const oldDesc) = 0; /** - * Never returns nullptr. + * Returns a pointer to the index catalog entry associated with 'desc'. Throws if there is no + * such index. Never returns nullptr. */ virtual const IndexCatalogEntry* getEntry(const IndexDescriptor* const desc) const = 0; + /** + * Returns a pointer to the index catalog entry associated with 'desc', where the caller assumes + * shared ownership of the entry. Returns null if the entry does not exist. + */ + virtual std::shared_ptr<const IndexCatalogEntry> getEntryShared( + const IndexDescriptor*) const = 0; + virtual IndexAccessMethod* getIndex(const IndexDescriptor* const desc) = 0; virtual const IndexAccessMethod* getIndex(const IndexDescriptor* const desc) const = 0; diff --git a/src/mongo/db/catalog/index_catalog_entry.cpp b/src/mongo/db/catalog/index_catalog_entry.cpp index 0f5f8dda58d..997cb4a4ba8 100644 --- a/src/mongo/db/catalog/index_catalog_entry.cpp +++ b/src/mongo/db/catalog/index_catalog_entry.cpp @@ -63,6 +63,16 @@ IndexCatalogEntry* IndexCatalogEntryContainer::find(const IndexDescriptor* desc) return nullptr; } +std::shared_ptr<IndexCatalogEntry> IndexCatalogEntryContainer::findShared( + const IndexDescriptor* desc) const { + for (auto&& entry : _entries) { + if (entry->descriptor() == desc) { + return entry; + } + } + return {}; +} + IndexCatalogEntry* IndexCatalogEntryContainer::find(const std::string& name) { for (iterator i = begin(); i != end(); ++i) { IndexCatalogEntry* e = i->get(); @@ -72,11 +82,12 @@ IndexCatalogEntry* IndexCatalogEntryContainer::find(const std::string& name) { return nullptr; } -IndexCatalogEntry* IndexCatalogEntryContainer::release(const IndexDescriptor* desc) { +std::shared_ptr<IndexCatalogEntry> IndexCatalogEntryContainer::release( + const IndexDescriptor* desc) { for (auto i = _entries.begin(); i != _entries.end(); ++i) { if ((*i)->descriptor() != desc) continue; - IndexCatalogEntry* e = i->release(); + auto e = std::move(*i); _entries.erase(i); return e; } diff --git a/src/mongo/db/catalog/index_catalog_entry.h b/src/mongo/db/catalog/index_catalog_entry.h index eb54f065e7b..192788e3948 100644 --- a/src/mongo/db/catalog/index_catalog_entry.h +++ b/src/mongo/db/catalog/index_catalog_entry.h @@ -156,8 +156,8 @@ public: class IndexCatalogEntryContainer { public: - typedef std::vector<std::unique_ptr<IndexCatalogEntry>>::const_iterator const_iterator; - typedef std::vector<std::unique_ptr<IndexCatalogEntry>>::const_iterator iterator; + typedef std::vector<std::shared_ptr<IndexCatalogEntry>>::const_iterator const_iterator; + typedef std::vector<std::shared_ptr<IndexCatalogEntry>>::const_iterator iterator; const_iterator begin() const { return _entries.begin(); @@ -182,6 +182,11 @@ public: IndexCatalogEntry* find(const std::string& name); + /** + * Returns a pointer to the IndexCatalogEntry corresponding to 'desc', where the caller assumes + * shared ownership of the catalog object. Returns null if the entry does not exist. + */ + std::shared_ptr<IndexCatalogEntry> findShared(const IndexDescriptor* desc) const; unsigned size() const { return _entries.size(); @@ -192,20 +197,17 @@ public: /** * Removes from _entries and returns the matching entry or NULL if none matches. */ - IndexCatalogEntry* release(const IndexDescriptor* desc); + std::shared_ptr<IndexCatalogEntry> release(const IndexDescriptor* desc); bool remove(const IndexDescriptor* desc) { - IndexCatalogEntry* entry = release(desc); - delete entry; - return entry; + return static_cast<bool>(release(desc)); } - // pass ownership to EntryContainer - void add(IndexCatalogEntry* entry) { - _entries.push_back(std::unique_ptr<IndexCatalogEntry>{entry}); + void add(std::shared_ptr<IndexCatalogEntry>&& entry) { + _entries.push_back(std::move(entry)); } private: - std::vector<std::unique_ptr<IndexCatalogEntry>> _entries; + std::vector<std::shared_ptr<IndexCatalogEntry>> _entries; }; } // namespace mongo diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 837ccfd7b4a..9a4aa990ab0 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -150,20 +150,20 @@ IndexCatalogEntry* IndexCatalogImpl::_setupInMemoryStructures( } auto* const descriptorPtr = descriptor.get(); - auto entry = stdx::make_unique<IndexCatalogEntryImpl>(opCtx, - _collection->ns().ns(), - _collection->getCatalogEntry(), - std::move(descriptor), - _collection->infoCache()); + auto entry = std::make_shared<IndexCatalogEntryImpl>(opCtx, + _collection->ns().ns(), + _collection->getCatalogEntry(), + std::move(descriptor), + _collection->infoCache()); std::unique_ptr<IndexAccessMethod> accessMethod( _collection->dbce()->getIndex(opCtx, _collection->getCatalogEntry(), entry.get())); entry->init(std::move(accessMethod)); IndexCatalogEntry* save = entry.get(); if (isReadyIndex) { - _readyIndexes.add(entry.release()); + _readyIndexes.add(std::move(entry)); } else { - _buildingIndexes.add(entry.release()); + _buildingIndexes.add(std::move(entry)); } if (!initFromDisk) { @@ -821,8 +821,8 @@ public: IndexRemoveChange(OperationContext* opCtx, Collection* collection, IndexCatalogEntryContainer* entries, - IndexCatalogEntry* entry) - : _opCtx(opCtx), _collection(collection), _entries(entries), _entry(entry) {} + std::shared_ptr<IndexCatalogEntry> entry) + : _opCtx(opCtx), _collection(collection), _entries(entries), _entry(std::move(entry)) {} void commit(boost::optional<Timestamp> commitTime) final { // Ban reading from this collection on committed reads on snapshots before now. @@ -834,20 +834,18 @@ public: commitTime = LogicalClock::getClusterTimeForReplicaSet(_opCtx).asTimestamp(); } _collection->setMinimumVisibleSnapshot(commitTime.get()); - - delete _entry; } void rollback() final { - _entries->add(_entry); _collection->infoCache()->addedIndex(_opCtx, _entry->descriptor()); + _entries->add(std::move(_entry)); } private: OperationContext* _opCtx; Collection* _collection; IndexCatalogEntryContainer* _entries; - IndexCatalogEntry* _entry; + std::shared_ptr<IndexCatalogEntry> _entry; }; } // namespace @@ -881,14 +879,16 @@ Status IndexCatalogImpl::_dropIndex(OperationContext* opCtx, IndexCatalogEntry* auto released = _readyIndexes.release(entry->descriptor()); if (released) { - invariant(released == entry); + invariant(released.get() == entry); opCtx->recoveryUnit()->registerChange( - new IndexRemoveChange(opCtx, _collection, &_readyIndexes, entry)); + new IndexRemoveChange(opCtx, _collection, &_readyIndexes, std::move(released))); } else { - invariant(_buildingIndexes.release(entry->descriptor()) == entry); + released = _buildingIndexes.release(entry->descriptor()); + invariant(released.get() == entry); opCtx->recoveryUnit()->registerChange( - new IndexRemoveChange(opCtx, _collection, &_buildingIndexes, entry)); + new IndexRemoveChange(opCtx, _collection, &_buildingIndexes, std::move(released))); } + _collection->infoCache()->droppedIndex(opCtx, indexName); entry = nullptr; _deleteIndexFromDisk(opCtx, indexName, indexNamespace); @@ -1100,6 +1100,14 @@ const IndexCatalogEntry* IndexCatalogImpl::getEntry(const IndexDescriptor* desc) return entry; } +std::shared_ptr<const IndexCatalogEntry> IndexCatalogImpl::getEntryShared( + const IndexDescriptor* indexDescriptor) const { + auto entry = _readyIndexes.findShared(indexDescriptor); + if (entry) { + return entry; + } + return _buildingIndexes.findShared(indexDescriptor); +} const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx, const IndexDescriptor* oldDesc) { @@ -1119,10 +1127,10 @@ const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx, // Delete the IndexCatalogEntry that owns this descriptor. After deletion, 'oldDesc' is // invalid and should not be dereferenced. - IndexCatalogEntry* oldEntry = _readyIndexes.release(oldDesc); + auto oldEntry = _readyIndexes.release(oldDesc); invariant(oldEntry); opCtx->recoveryUnit()->registerChange( - new IndexRemoveChange(opCtx, _collection, &_readyIndexes, oldEntry)); + new IndexRemoveChange(opCtx, _collection, &_readyIndexes, std::move(oldEntry))); // Ask the CollectionCatalogEntry for the new index spec. BSONObj spec = _collection->getCatalogEntry()->getIndexSpec(opCtx, indexName).getOwned(); @@ -1348,11 +1356,13 @@ void IndexCatalogImpl::prepareInsertDeleteOptions(OperationContext* opCtx, } void IndexCatalogImpl::indexBuildSuccess(OperationContext* opCtx, IndexCatalogEntry* index) { - invariant(_buildingIndexes.release(index->descriptor())); - _readyIndexes.add(index); + auto releasedEntry = _buildingIndexes.release(index->descriptor()); + invariant(releasedEntry.get() == index); + _readyIndexes.add(std::move(releasedEntry)); opCtx->recoveryUnit()->onRollback([this, index]() { - invariant(_readyIndexes.release(index->descriptor())); - _buildingIndexes.add(index); + auto releasedEntry = _readyIndexes.release(index->descriptor()); + invariant(releasedEntry.get() == index); + _buildingIndexes.add(std::move(releasedEntry)); }); } diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h index f37e145c5b9..7c6c415241f 100644 --- a/src/mongo/db/catalog/index_catalog_impl.h +++ b/src/mongo/db/catalog/index_catalog_impl.h @@ -162,9 +162,10 @@ public: const IndexDescriptor* refreshEntry(OperationContext* opCtx, const IndexDescriptor* oldDesc) override; - // never returns NULL const IndexCatalogEntry* getEntry(const IndexDescriptor* desc) const override; + std::shared_ptr<const IndexCatalogEntry> getEntryShared(const IndexDescriptor*) const override; + IndexAccessMethod* getIndex(const IndexDescriptor* desc) override; const IndexAccessMethod* getIndex(const IndexDescriptor* desc) const override; diff --git a/src/mongo/db/catalog/index_catalog_noop.h b/src/mongo/db/catalog/index_catalog_noop.h index 29ad037e221..e645831063a 100644 --- a/src/mongo/db/catalog/index_catalog_noop.h +++ b/src/mongo/db/catalog/index_catalog_noop.h @@ -115,6 +115,10 @@ public: return nullptr; } + std::shared_ptr<const IndexCatalogEntry> getEntryShared(const IndexDescriptor*) const override { + return nullptr; + } + IndexAccessMethod* getIndex(const IndexDescriptor* const desc) override { return nullptr; } diff --git a/src/mongo/db/exec/cached_plan.h b/src/mongo/db/exec/cached_plan.h index ed6260c5f72..d1c6fcaaf8f 100644 --- a/src/mongo/db/exec/cached_plan.h +++ b/src/mongo/db/exec/cached_plan.h @@ -87,9 +87,9 @@ public: Status pickBestPlan(PlanYieldPolicy* yieldPolicy); protected: - void saveState(RequiresCollTag) final {} + void doSaveStateRequiresCollection() final {} - void restoreState(RequiresCollTag) final {} + void doRestoreStateRequiresCollection() final {} private: /** diff --git a/src/mongo/db/exec/collection_scan.cpp b/src/mongo/db/exec/collection_scan.cpp index 3f395d6fa21..f507a24dba4 100644 --- a/src/mongo/db/exec/collection_scan.cpp +++ b/src/mongo/db/exec/collection_scan.cpp @@ -211,13 +211,13 @@ bool CollectionScan::isEOF() { return _commonStats.isEOF; } -void CollectionScan::saveState(RequiresCollTag) { +void CollectionScan::doSaveStateRequiresCollection() { if (_cursor) { _cursor->save(); } } -void CollectionScan::restoreState(RequiresCollTag) { +void CollectionScan::doRestoreStateRequiresCollection() { if (_cursor) { const bool couldRestore = _cursor->restore(); uassert(ErrorCodes::CappedPositionLost, diff --git a/src/mongo/db/exec/collection_scan.h b/src/mongo/db/exec/collection_scan.h index 1c7138256e0..3752df9db13 100644 --- a/src/mongo/db/exec/collection_scan.h +++ b/src/mongo/db/exec/collection_scan.h @@ -79,9 +79,9 @@ public: const SpecificStats* getSpecificStats() const final; protected: - void saveState(RequiresCollTag) final; + void doSaveStateRequiresCollection() final; - void restoreState(RequiresCollTag) final; + void doRestoreStateRequiresCollection() final; private: /** diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp index a279cdd27f7..49bbef02748 100644 --- a/src/mongo/db/exec/delete.cpp +++ b/src/mongo/db/exec/delete.cpp @@ -258,7 +258,7 @@ PlanStage::StageState DeleteStage::doWork(WorkingSetID* out) { return PlanStage::NEED_TIME; } -void DeleteStage::restoreState(RequiresCollTag) { +void DeleteStage::doRestoreStateRequiresCollection() { const NamespaceString& ns = collection()->ns(); uassert(ErrorCodes::PrimarySteppedDown, str::stream() << "Demoted from primary while removing from " << ns.ns(), diff --git a/src/mongo/db/exec/delete.h b/src/mongo/db/exec/delete.h index c3fd5ee5cba..4fe3590745d 100644 --- a/src/mongo/db/exec/delete.h +++ b/src/mongo/db/exec/delete.h @@ -116,9 +116,9 @@ public: static long long getNumDeleted(const PlanExecutor& exec); protected: - void saveState(RequiresCollTag) final {} + void doSaveStateRequiresCollection() final {} - void restoreState(RequiresCollTag) final; + void doRestoreStateRequiresCollection() final; private: /** diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp index 90654b11d92..1a41badc768 100644 --- a/src/mongo/db/exec/fetch.cpp +++ b/src/mongo/db/exec/fetch.cpp @@ -132,13 +132,13 @@ PlanStage::StageState FetchStage::doWork(WorkingSetID* out) { return status; } -void FetchStage::saveState(RequiresCollTag) { +void FetchStage::doSaveStateRequiresCollection() { if (_cursor) { _cursor->saveUnpositioned(); } } -void FetchStage::restoreState(RequiresCollTag) { +void FetchStage::doRestoreStateRequiresCollection() { if (_cursor) { const bool couldRestore = _cursor->restore(); uassert(50982, "could not restore cursor for FETCH stage", couldRestore); diff --git a/src/mongo/db/exec/fetch.h b/src/mongo/db/exec/fetch.h index ea55f13892f..52b3a71d083 100644 --- a/src/mongo/db/exec/fetch.h +++ b/src/mongo/db/exec/fetch.h @@ -76,9 +76,9 @@ public: static const char* kStageType; protected: - void saveState(RequiresCollTag) final; + void doSaveStateRequiresCollection() final; - void restoreState(RequiresCollTag) final; + void doRestoreStateRequiresCollection() final; private: /** diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index 2a6524151cf..5725fc9d9f9 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -316,7 +316,7 @@ void GeoNear2DStage::DensityEstimator::buildIndexScan(OperationContext* opCtx, WorkingSet* workingSet) { // Scan bounds on 2D indexes are only over the 2D field - other bounds aren't applicable. // This is handled in query planning. - IndexScanParams scanParams(opCtx, *_twoDIndex); + IndexScanParams scanParams(opCtx, _twoDIndex); scanParams.bounds = _nearParams->baseBounds; // The "2d" field is always the first in the index @@ -684,7 +684,7 @@ StatusWith<NearStage::CoveredInterval*> // // Scan bounds on 2D indexes are only over the 2D field - other bounds aren't applicable. // This is handled in query planning. - IndexScanParams scanParams(opCtx, *_twoDIndex); + IndexScanParams scanParams(opCtx, _twoDIndex); // This does force us to do our own deduping of results. scanParams.bounds = _nearParams.baseBounds; @@ -874,7 +874,7 @@ private: // Setup the index scan stage for neighbors at this level. void GeoNear2DSphereStage::DensityEstimator::buildIndexScan(OperationContext* opCtx, WorkingSet* workingSet) { - IndexScanParams scanParams(opCtx, *_s2Index); + IndexScanParams scanParams(opCtx, _s2Index); scanParams.bounds = _nearParams->baseBounds; // Because the planner doesn't yet set up 2D index bounds, do it ourselves here @@ -1044,7 +1044,7 @@ StatusWith<NearStage::CoveredInterval*> // // Setup the covering region and stages for this interval // - IndexScanParams scanParams(opCtx, *_s2Index); + IndexScanParams scanParams(opCtx, _s2Index); // This does force us to do our own deduping of results. scanParams.bounds = _nearParams.baseBounds; diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp index 7594b5af011..11bf51dabb4 100644 --- a/src/mongo/db/exec/index_scan.cpp +++ b/src/mongo/db/exec/index_scan.cpp @@ -65,38 +65,41 @@ IndexScan::IndexScan(OperationContext* opCtx, IndexScanParams params, WorkingSet* workingSet, const MatchExpression* filter) - : PlanStage(kStageType, opCtx), + : RequiresIndexStage(kStageType, opCtx, params.indexDescriptor), _workingSet(workingSet), - _iam(params.accessMethod), _keyPattern(params.keyPattern.getOwned()), - _scanState(INITIALIZING), + _bounds(std::move(params.bounds)), _filter(filter), + _direction(params.direction), _forward(params.direction == 1), - _params(std::move(params)), - _startKeyInclusive(IndexBounds::isStartIncludedInBound(_params.bounds.boundInclusion)), - _endKeyInclusive(IndexBounds::isEndIncludedInBound(_params.bounds.boundInclusion)) { - _specificStats.indexName = _params.name; + _shouldDedup(params.shouldDedup), + _addKeyMetadata(params.addKeyMetadata), + _startKeyInclusive(IndexBounds::isStartIncludedInBound(params.bounds.boundInclusion)), + _endKeyInclusive(IndexBounds::isEndIncludedInBound(params.bounds.boundInclusion)) { + _specificStats.indexName = params.name; _specificStats.keyPattern = _keyPattern; - _specificStats.isMultiKey = _params.isMultiKey; - _specificStats.multiKeyPaths = _params.multikeyPaths; - _specificStats.isUnique = _params.isUnique; - _specificStats.isSparse = _params.isSparse; - _specificStats.isPartial = _params.isPartial; - _specificStats.indexVersion = static_cast<int>(_params.version); - _specificStats.collation = _params.collation.getOwned(); + _specificStats.isMultiKey = params.isMultiKey; + _specificStats.multiKeyPaths = params.multikeyPaths; + _specificStats.isUnique = params.indexDescriptor->unique(); + _specificStats.isSparse = params.indexDescriptor->isSparse(); + _specificStats.isPartial = params.indexDescriptor->isPartial(); + _specificStats.indexVersion = static_cast<int>(params.indexDescriptor->version()); + _specificStats.collation = params.indexDescriptor->infoObj() + .getObjectField(IndexDescriptor::kCollationFieldName) + .getOwned(); } boost::optional<IndexKeyEntry> IndexScan::initIndexScan() { // Perform the possibly heavy-duty initialization of the underlying index cursor. - _indexCursor = _iam->newCursor(getOpCtx(), _forward); + _indexCursor = indexAccessMethod()->newCursor(getOpCtx(), _forward); // We always seek once to establish the cursor position. ++_specificStats.seeks; - if (_params.bounds.isSimpleRange) { + if (_bounds.isSimpleRange) { // Start at one key, end at another. - _startKey = _params.bounds.startKey; - _endKey = _params.bounds.endKey; + _startKey = _bounds.startKey; + _endKey = _bounds.endKey; _indexCursor->setEndPosition(_endKey, _endKeyInclusive); return _indexCursor->seek(_startKey, _startKeyInclusive); } else { @@ -104,11 +107,11 @@ boost::optional<IndexKeyEntry> IndexScan::initIndexScan() { // of an end cursor. For all other index scans, we fall back on using // IndexBoundsChecker to determine when we've finished the scan. if (IndexBoundsBuilder::isSingleInterval( - _params.bounds, &_startKey, &_startKeyInclusive, &_endKey, &_endKeyInclusive)) { + _bounds, &_startKey, &_startKeyInclusive, &_endKey, &_endKeyInclusive)) { _indexCursor->setEndPosition(_endKey, _endKeyInclusive); return _indexCursor->seek(_startKey, _startKeyInclusive); } else { - _checker.reset(new IndexBoundsChecker(&_params.bounds, _keyPattern, _params.direction)); + _checker.reset(new IndexBoundsChecker(&_bounds, _keyPattern, _direction)); if (!_checker->getStartSeekPoint(&_seekPoint)) return boost::none; @@ -188,7 +191,7 @@ PlanStage::StageState IndexScan::doWork(WorkingSetID* out) { _scanState = GETTING_NEXT; - if (_params.shouldDedup) { + if (_shouldDedup) { ++_specificStats.dupsTested; if (!_returned.insert(kv->loc).second) { // We've seen this RecordId before. Skip it this time. @@ -210,10 +213,10 @@ PlanStage::StageState IndexScan::doWork(WorkingSetID* out) { WorkingSetID id = _workingSet->allocate(); WorkingSetMember* member = _workingSet->get(id); member->recordId = kv->loc; - member->keyData.push_back(IndexKeyDatum(_keyPattern, kv->key, _iam)); + member->keyData.push_back(IndexKeyDatum(_keyPattern, kv->key, indexAccessMethod())); _workingSet->transitionToRecordIdAndIdx(id); - if (_params.addKeyMetadata) { + if (_addKeyMetadata) { member->addComputed( new IndexKeyComputedData(IndexKeyComputedData::rehydrateKey(_keyPattern, kv->key))); } @@ -226,7 +229,7 @@ bool IndexScan::isEOF() { return _commonStats.isEOF; } -void IndexScan::doSaveState() { +void IndexScan::doSaveStateRequiresIndex() { if (!_indexCursor) return; @@ -238,7 +241,7 @@ void IndexScan::doSaveState() { _indexCursor->save(); } -void IndexScan::doRestoreState() { +void IndexScan::doRestoreStateRequiresIndex() { if (_indexCursor) _indexCursor->restore(); } @@ -268,9 +271,9 @@ std::unique_ptr<PlanStageStats> IndexScan::getStats() { if (_specificStats.indexType.empty()) { _specificStats.indexType = "BtreeCursor"; // TODO amName; - _specificStats.indexBounds = _params.bounds.toBSON(); + _specificStats.indexBounds = _bounds.toBSON(); - _specificStats.direction = _params.direction; + _specificStats.direction = _direction; } std::unique_ptr<PlanStageStats> ret = diff --git a/src/mongo/db/exec/index_scan.h b/src/mongo/db/exec/index_scan.h index 40f790e6034..3719ade1055 100644 --- a/src/mongo/db/exec/index_scan.h +++ b/src/mongo/db/exec/index_scan.h @@ -30,7 +30,7 @@ #pragma once -#include "mongo/db/exec/plan_stage.h" +#include "mongo/db/exec/requires_index_stage.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/jsobj.h" #include "mongo/db/matcher/expression.h" @@ -45,49 +45,35 @@ namespace mongo { class WorkingSet; struct IndexScanParams { - IndexScanParams(const IndexDescriptor& descriptor, + IndexScanParams(const IndexDescriptor* descriptor, std::string indexName, BSONObj keyPattern, MultikeyPaths multikeyPaths, bool multikey) - : accessMethod(descriptor.getIndexCatalog()->getIndex(&descriptor)), + : indexDescriptor(descriptor), name(std::move(indexName)), keyPattern(std::move(keyPattern)), multikeyPaths(std::move(multikeyPaths)), - isMultiKey(multikey), - isSparse(descriptor.isSparse()), - isUnique(descriptor.unique()), - isPartial(descriptor.isPartial()), - version(descriptor.version()), - collation(descriptor.infoObj() - .getObjectField(IndexDescriptor::kCollationFieldName) - .getOwned()) { - invariant(accessMethod); - } + isMultiKey(multikey) {} - IndexScanParams(OperationContext* opCtx, const IndexDescriptor& descriptor) + IndexScanParams(OperationContext* opCtx, const IndexDescriptor* descriptor) : IndexScanParams(descriptor, - descriptor.indexName(), - descriptor.keyPattern(), - descriptor.getMultikeyPaths(opCtx), - descriptor.isMultikey(opCtx)) {} + descriptor->indexName(), + descriptor->keyPattern(), + descriptor->getMultikeyPaths(opCtx), + descriptor->isMultikey(opCtx)) {} + + const IndexDescriptor* indexDescriptor; - const IndexAccessMethod* accessMethod; std::string name; BSONObj keyPattern; - IndexBounds bounds; MultikeyPaths multikeyPaths; - bool isMultiKey; - - bool isSparse; - bool isUnique; - bool isPartial; - IndexDescriptor::IndexVersion version; + bool isMultiKey; - BSONObj collation; + IndexBounds bounds; int direction{1}; @@ -103,7 +89,7 @@ struct IndexScanParams { * * Sub-stage preconditions: None. Is a leaf and consumes no stage data. */ -class IndexScan final : public PlanStage { +class IndexScan final : public RequiresIndexStage { public: /** * Keeps track of what this index scan is currently doing so that it @@ -130,8 +116,6 @@ public: StageState doWork(WorkingSetID* out) final; bool isEOF() final; - void doSaveState() final; - void doRestoreState() final; void doDetachFromOperationContext() final; void doReattachToOperationContext() final; @@ -145,6 +129,11 @@ public: static const char* kStageType; +protected: + void doSaveStateRequiresIndex() final; + + void doRestoreStateRequiresIndex() final; + private: /** * Initialize the underlying index Cursor, returning first result if any. @@ -154,28 +143,33 @@ private: // The WorkingSet we fill with results. Not owned by us. WorkingSet* const _workingSet; - // Index access. - const IndexAccessMethod* const _iam; // owned by Collection -> IndexCatalog std::unique_ptr<SortedDataInterface::Cursor> _indexCursor; const BSONObj _keyPattern; - // Keeps track of what work we need to do next. - ScanState _scanState; + const IndexBounds _bounds; // Contains expressions only over fields in the index key. We assume this is built // correctly by whomever creates this class. // The filter is not owned by us. const MatchExpression* const _filter; - // Could our index have duplicates? If so, we use _returned to dedup. - stdx::unordered_set<RecordId, RecordId::Hasher> _returned; - + const int _direction; const bool _forward; - const IndexScanParams _params; + + const bool _shouldDedup; + + // Do we want to add the key as metadata? + const bool _addKeyMetadata; // Stats IndexScanStats _specificStats; + // Keeps track of what work we need to do next. + ScanState _scanState = ScanState::INITIALIZING; + + // Could our index have duplicates? If so, we use _returned to dedup. + stdx::unordered_set<RecordId, RecordId::Hasher> _returned; + // // This class employs one of two different algorithms for determining when the index scan // has reached the end: diff --git a/src/mongo/db/exec/multi_iterator.cpp b/src/mongo/db/exec/multi_iterator.cpp index f3222d3fbc5..707e463204f 100644 --- a/src/mongo/db/exec/multi_iterator.cpp +++ b/src/mongo/db/exec/multi_iterator.cpp @@ -84,13 +84,13 @@ bool MultiIteratorStage::isEOF() { return _iterators.empty(); } -void MultiIteratorStage::saveState(RequiresCollTag) { +void MultiIteratorStage::doSaveStateRequiresCollection() { for (auto&& iterator : _iterators) { iterator->save(); } } -void MultiIteratorStage::restoreState(RequiresCollTag) { +void MultiIteratorStage::doRestoreStateRequiresCollection() { for (auto&& iterator : _iterators) { const bool couldRestore = iterator->restore(); uassert(50991, "could not restore cursor for MULTI_ITERATOR stage", couldRestore); diff --git a/src/mongo/db/exec/multi_iterator.h b/src/mongo/db/exec/multi_iterator.h index 5b9b9aeef6c..6e84c8253fb 100644 --- a/src/mongo/db/exec/multi_iterator.h +++ b/src/mongo/db/exec/multi_iterator.h @@ -75,9 +75,9 @@ public: static const char* kStageType; protected: - void saveState(RequiresCollTag) final; + void doSaveStateRequiresCollection() final; - void restoreState(RequiresCollTag) final; + void doRestoreStateRequiresCollection() final; private: OperationContext* _opCtx; diff --git a/src/mongo/db/exec/multi_plan.h b/src/mongo/db/exec/multi_plan.h index c24390c24c7..9693082b529 100644 --- a/src/mongo/db/exec/multi_plan.h +++ b/src/mongo/db/exec/multi_plan.h @@ -154,9 +154,9 @@ public: static const char* kStageType; protected: - void saveState(RequiresCollTag) final {} + void doSaveStateRequiresCollection() final {} - void restoreState(RequiresCollTag) final {} + void doRestoreStateRequiresCollection() final {} private: // diff --git a/src/mongo/db/exec/near.h b/src/mongo/db/exec/near.h index 36ec727de74..9052e5d7d26 100644 --- a/src/mongo/db/exec/near.h +++ b/src/mongo/db/exec/near.h @@ -145,9 +145,9 @@ protected: WorkingSet* workingSet, WorkingSetID* out) = 0; - void saveState(RequiresCollTag) final {} + void doSaveStateRequiresCollection() final {} - void restoreState(RequiresCollTag) final {} + void doRestoreStateRequiresCollection() final {} // Filled in by subclasses. NearStats _specificStats; diff --git a/src/mongo/db/exec/record_store_fast_count.h b/src/mongo/db/exec/record_store_fast_count.h index b3578ae5f30..ab601569cd4 100644 --- a/src/mongo/db/exec/record_store_fast_count.h +++ b/src/mongo/db/exec/record_store_fast_count.h @@ -64,9 +64,9 @@ public: } protected: - void saveState(RequiresCollTag) override {} + void doSaveStateRequiresCollection() override {} - void restoreState(RequiresCollTag) override {} + void doRestoreStateRequiresCollection() override {} private: long long _skip = 0; diff --git a/src/mongo/db/exec/requires_collection_stage.cpp b/src/mongo/db/exec/requires_collection_stage.cpp index 9424f542839..5825485dea6 100644 --- a/src/mongo/db/exec/requires_collection_stage.cpp +++ b/src/mongo/db/exec/requires_collection_stage.cpp @@ -37,10 +37,10 @@ namespace mongo { template <typename CollectionT> void RequiresCollectionStageBase<CollectionT>::doSaveState() { + doSaveStateRequiresCollection(); + // A stage may not access storage while in a saved state. _collection = nullptr; - - saveState(RequiresCollTag{}); } template <typename CollectionT> @@ -53,7 +53,7 @@ void RequiresCollectionStageBase<CollectionT>::doRestoreState() { str::stream() << "UUID " << _collectionUUID << " no longer exists.", _collection); - restoreState(RequiresCollTag{}); + doRestoreStateRequiresCollection(); } template class RequiresCollectionStageBase<const Collection*>; diff --git a/src/mongo/db/exec/requires_collection_stage.h b/src/mongo/db/exec/requires_collection_stage.h index 273a7185ae4..9ebd96f84f0 100644 --- a/src/mongo/db/exec/requires_collection_stage.h +++ b/src/mongo/db/exec/requires_collection_stage.h @@ -41,7 +41,7 @@ namespace mongo { * for checking that the collection is still valid (e.g. has not been dropped) when recovering from * yield. * - * Subclasses must implement the saveStage() and restoreState() variants tagged with RequiresCollTag + * Subclasses must implement doSaveStateRequiresCollection() and doRestoreStateRequiresCollection() * in order to supply custom yield preparation or yield recovery logic. * * Templated on 'CollectionT', which may be instantiated using either Collection* or const @@ -63,8 +63,6 @@ public: virtual ~RequiresCollectionStageBase() = default; protected: - struct RequiresCollTag {}; - void doSaveState() final; void doRestoreState() final; @@ -72,12 +70,12 @@ protected: /** * Performs yield preparation specific to a stage which subclasses from RequiresCollectionStage. */ - virtual void saveState(RequiresCollTag) = 0; + virtual void doSaveStateRequiresCollection() = 0; /** * Performs yield recovery specific to a stage which subclasses from RequiresCollectionStage. */ - virtual void restoreState(RequiresCollTag) = 0; + virtual void doRestoreStateRequiresCollection() = 0; CollectionT collection() const { return _collection; diff --git a/src/mongo/db/exec/requires_index_stage.cpp b/src/mongo/db/exec/requires_index_stage.cpp new file mode 100644 index 00000000000..4c7150e1920 --- /dev/null +++ b/src/mongo/db/exec/requires_index_stage.cpp @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/exec/requires_index_stage.h" + +namespace mongo { + +void RequiresIndexStage::doSaveStateRequiresCollection() { + doSaveStateRequiresIndex(); + + // During yield, we relinquish our shared ownership of the index catalog entry. This allows the + // index to be dropped during yield, but permits us to check via the weak_ptr interface + // whether the index is still valid on yield recovery. + // + // We also set catalog pointers to null, since accessing these pointers is illegal during yield. + _indexCatalogEntry.reset(); + _indexDescriptor = nullptr; + _indexAccessMethod = nullptr; +} + +void RequiresIndexStage::doRestoreStateRequiresCollection() { + // Reacquire shared ownership of the index catalog entry. If we're unable to do so, then the + // our index is no longer valid, and the query should die. + _indexCatalogEntry = _weakIndexCatalogEntry.lock(); + uassert(ErrorCodes::QueryPlanKilled, + str::stream() << "query plan killed :: index named '" << _indexName + << "' is no longer valid", + _indexCatalogEntry); + + _indexDescriptor = _indexCatalogEntry->descriptor(); + _indexAccessMethod = _indexCatalogEntry->accessMethod(); + + doRestoreStateRequiresIndex(); +} + +} // namespace mongo diff --git a/src/mongo/db/exec/requires_index_stage.h b/src/mongo/db/exec/requires_index_stage.h new file mode 100644 index 00000000000..320a43a6444 --- /dev/null +++ b/src/mongo/db/exec/requires_index_stage.h @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/db/exec/requires_collection_stage.h" + +namespace mongo { + +/** + * A base class for plan stages which require access to a particular index within a particular + * collection. Provides subclasses access to the index's Collection*, as well as to catalog types + * representing the index itself such as the IndexDescriptor. This base class is responsible for + * checking that the collection and index are still valid (e.g. have not been dropped) when + * recovering from yield. + * + * Subclasses must implement doSaveStateRequiresIndex() and doRestoreStateRequiresIndex() in order + * to supply custom yield preparation and yield recovery logic. + */ +class RequiresIndexStage : public RequiresCollectionStage { +public: + RequiresIndexStage(const char* stageType, + OperationContext* opCtx, + const IndexDescriptor* indexDescriptor) + : RequiresCollectionStage(stageType, opCtx, indexDescriptor->getCollection()), + _weakIndexCatalogEntry(collection()->getIndexCatalog()->getEntryShared(indexDescriptor)), + _indexCatalogEntry(_weakIndexCatalogEntry.lock()), + _indexDescriptor(indexDescriptor), + _indexAccessMethod(_indexCatalogEntry->accessMethod()), + _indexName(_indexDescriptor->indexName()) { + invariant(_indexCatalogEntry); + invariant(_indexDescriptor); + invariant(_indexAccessMethod); + } + + virtual ~RequiresIndexStage() = default; + +protected: + /** + * Performs yield preparation specific to a stage which subclasses from RequiresIndexStage. + */ + virtual void doSaveStateRequiresIndex() = 0; + + /** + * Performs yield recovery specific to a stage which subclasses from RequiresIndexStage. + */ + virtual void doRestoreStateRequiresIndex() = 0; + + void doSaveStateRequiresCollection() override final; + + void doRestoreStateRequiresCollection() override final; + + const IndexDescriptor* indexDescriptor() const { + return _indexDescriptor; + } + + const IndexAccessMethod* indexAccessMethod() const { + return _indexAccessMethod; + } + +private: + // This stage shares ownership of the index catalog entry when the query is running, and + // relinquishes its shared ownership when in a saved state for a yield or between getMores. We + // keep a weak_ptr to the entry in order to reacquire shared ownership on yield recovery, + // throwing a query-fatal exception if the weak_ptr indicates that the underlying catalog object + // has been destroyed. + // + // This is necessary to protect against that case that our index is dropped and then recreated + // during yield. Such an event should cause the query to be killed, since index cursors may have + // pointers into catalog objects that no longer exist. Since indices do not have UUIDs, + // different epochs of the index cannot be distinguished. The weak_ptr allows us to relinquish + // ownership of the index during yield, but also determine whether the pointed-to object has + // been destroyed during yield recovery. + std::weak_ptr<const IndexCatalogEntry> _weakIndexCatalogEntry; + std::shared_ptr<const IndexCatalogEntry> _indexCatalogEntry; + + const IndexDescriptor* _indexDescriptor; + const IndexAccessMethod* _indexAccessMethod; + + const std::string _indexName; +}; + +} // namespace mongo diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp index df13e4a5985..ebcd2208952 100644 --- a/src/mongo/db/exec/stagedebug_cmd.cpp +++ b/src/mongo/db/exec/stagedebug_cmd.cpp @@ -302,7 +302,7 @@ public: uassert(40223, str::stream() << "Can't find index: " << name.toString(), desc); } - IndexScanParams params(opCtx, *desc); + IndexScanParams params(opCtx, desc); params.bounds.isSimpleRange = true; params.bounds.startKey = stripFieldNames(nodeArgs["startKey"].Obj()); params.bounds.endKey = stripFieldNames(nodeArgs["endKey"].Obj()); diff --git a/src/mongo/db/exec/subplan.h b/src/mongo/db/exec/subplan.h index 799cf15638d..3f589a2b8ef 100644 --- a/src/mongo/db/exec/subplan.h +++ b/src/mongo/db/exec/subplan.h @@ -128,9 +128,9 @@ public: } protected: - void saveState(RequiresCollTag) final {} + void doSaveStateRequiresCollection() final {} - void restoreState(RequiresCollTag) final {} + void doRestoreStateRequiresCollection() final {} private: /** diff --git a/src/mongo/db/exec/text.cpp b/src/mongo/db/exec/text.cpp index 4cd25adad23..015b517b005 100644 --- a/src/mongo/db/exec/text.cpp +++ b/src/mongo/db/exec/text.cpp @@ -103,7 +103,7 @@ unique_ptr<PlanStage> TextStage::buildTextTree(OperationContext* opCtx, // Get all the index scans for each term in our query. std::vector<std::unique_ptr<PlanStage>> indexScanList; for (const auto& term : _params.query.getTermsForBounds()) { - IndexScanParams ixparams(opCtx, *_params.index); + IndexScanParams ixparams(opCtx, _params.index); ixparams.bounds.startKey = FTSIndexFormat::getIndexKey( MAX_WEIGHT, term, _params.indexPrefix, _params.spec.getTextIndexVersion()); ixparams.bounds.endKey = FTSIndexFormat::getIndexKey( diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp index 87b79fd5b8b..2a06aa37aa2 100644 --- a/src/mongo/db/exec/update.cpp +++ b/src/mongo/db/exec/update.cpp @@ -692,7 +692,7 @@ PlanStage::StageState UpdateStage::doWork(WorkingSetID* out) { return status; } -void UpdateStage::restoreState(RequiresCollTag) { +void UpdateStage::doRestoreStateRequiresCollection() { const UpdateRequest& request = *_params.request; const NamespaceString& nsString(request.getNamespaceString()); diff --git a/src/mongo/db/exec/update.h b/src/mongo/db/exec/update.h index 21ee75e2612..dc228521fa6 100644 --- a/src/mongo/db/exec/update.h +++ b/src/mongo/db/exec/update.h @@ -145,9 +145,9 @@ public: UpdateStats* stats); protected: - void saveState(RequiresCollTag) final {} + void doSaveStateRequiresCollection() final {} - void restoreState(RequiresCollTag) final; + void doRestoreStateRequiresCollection() final; private: static const UpdateStats kEmptyUpdateStats; diff --git a/src/mongo/db/query/internal_plans.cpp b/src/mongo/db/query/internal_plans.cpp index dff69fc1298..6f4d976bd87 100644 --- a/src/mongo/db/query/internal_plans.cpp +++ b/src/mongo/db/query/internal_plans.cpp @@ -207,7 +207,7 @@ std::unique_ptr<PlanStage> InternalPlanner::_indexScan(OperationContext* opCtx, invariant(collection); invariant(descriptor); - IndexScanParams params(opCtx, *descriptor); + IndexScanParams params(opCtx, descriptor); params.direction = direction; params.bounds.isSimpleRange = true; params.bounds.startKey = startKey; diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp index ab5de811f62..0ea2780935e 100644 --- a/src/mongo/db/query/stage_builder.cpp +++ b/src/mongo/db/query/stage_builder.cpp @@ -98,7 +98,7 @@ PlanStage* buildStages(OperationContext* opCtx, // We use the node's internal name, keyPattern and multikey details here. For $** // indexes, these may differ from the information recorded in the index's descriptor. - IndexScanParams params{*descriptor, + IndexScanParams params{descriptor, ixn->index.identifier.catalogName, ixn->index.keyPattern, ixn->index.multikeyPaths, |