diff options
Diffstat (limited to 'src/mongo/db/catalog/index_catalog_impl.cpp')
| -rw-r--r-- | src/mongo/db/catalog/index_catalog_impl.cpp | 266 |
1 files changed, 69 insertions, 197 deletions
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 2c3086a54cf..6733dac0ba1 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -47,6 +47,7 @@ #include "mongo/db/catalog/uncommitted_catalog_updates.h" #include "mongo/db/client.h" #include "mongo/db/clientcursor.h" +#include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/curop.h" #include "mongo/db/field_ref.h" #include "mongo/db/fts/fts_spec.h" @@ -204,30 +205,13 @@ Status IndexCatalogImpl::init(OperationContext* opCtx, Collection* collection) { auto descriptor = std::make_unique<IndexDescriptor>(_getAccessMethodName(keyPattern), spec); - // TTL indexes with NaN 'expireAfterSeconds' cause problems in multiversion settings. - if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) { - if (spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()) { - LOGV2_OPTIONS(6852200, - {logv2::LogTag::kStartupWarnings}, - "Found an existing TTL index with NaN 'expireAfterSeconds' in the " - "catalog.", - "ns"_attr = collection->ns(), - "uuid"_attr = collection->uuid(), - "index"_attr = indexName, - "spec"_attr = spec); - } - } - // TTL indexes are not compatible with capped collections. // Note that TTL deletion is supported on capped clustered collections via bounded // collection scan, which does not use an index. if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName) && !collection->isCapped()) { TTLCollectionCache::get(opCtx->getServiceContext()) - .registerTTLInfo( - collection->uuid(), - TTLCollectionCache::Info{ - indexName, spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()}); + .registerTTLInfo(collection->uuid(), indexName); } bool ready = collection->isIndexReady(indexName); @@ -273,8 +257,8 @@ Status IndexCatalogImpl::init(OperationContext* opCtx, Collection* collection) { } std::unique_ptr<IndexCatalog::IndexIterator> IndexCatalogImpl::getIndexIterator( - OperationContext* const opCtx, InclusionPolicy inclusionPolicy) const { - if (inclusionPolicy == InclusionPolicy::kReady) { + OperationContext* const opCtx, const bool includeUnfinishedIndexes) const { + if (!includeUnfinishedIndexes) { // If the caller only wants the ready indexes, we return an iterator over the catalog's // ready indexes vector. When the user advances this iterator, it will filter out any // indexes that were not ready at the OperationContext's read timestamp. @@ -282,28 +266,17 @@ std::unique_ptr<IndexCatalog::IndexIterator> IndexCatalogImpl::getIndexIterator( opCtx, _readyIndexes.begin(), _readyIndexes.end()); } - // If the caller doesn't only want the ready indexes, for simplicity of implementation, we copy - // the pointers to a new vector. The vector's ownership is passed to the iterator. The query - // code path from an external client is not expected to hit this case so the cost isn't paid by - // the important code path. + // If the caller wants all indexes, for simplicity of implementation, we copy the pointers to + // a new vector. The vector's ownership is passed to the iterator. The query code path from an + // external client is not expected to hit this case so the cost isn't paid by the important + // code path. auto allIndexes = std::make_unique<std::vector<IndexCatalogEntry*>>(); - - if (inclusionPolicy & InclusionPolicy::kReady) { - for (auto it = _readyIndexes.begin(); it != _readyIndexes.end(); ++it) { - allIndexes->push_back(it->get()); - } + for (auto it = _readyIndexes.begin(); it != _readyIndexes.end(); ++it) { + allIndexes->push_back(it->get()); } - if (inclusionPolicy & InclusionPolicy::kUnfinished) { - for (auto it = _buildingIndexes.begin(); it != _buildingIndexes.end(); ++it) { - allIndexes->push_back(it->get()); - } - } - - if (inclusionPolicy & InclusionPolicy::kFrozen) { - for (auto it = _frozenIndexes.begin(); it != _frozenIndexes.end(); ++it) { - allIndexes->push_back(it->get()); - } + for (auto it = _buildingIndexes.begin(); it != _buildingIndexes.end(); ++it) { + allIndexes->push_back(it->get()); } return std::make_unique<AllIndexesIterator>(opCtx, std::move(allIndexes)); @@ -377,7 +350,6 @@ void IndexCatalogImpl::_logInternalState(OperationContext* opCtx, "numIndexesInCollectionCatalogEntry"_attr = numIndexesInCollectionCatalogEntry, "numReadyIndexes"_attr = _readyIndexes.size(), "numBuildingIndexes"_attr = _buildingIndexes.size(), - "numFrozenIndexes"_attr = _frozenIndexes.size(), "indexNamesToDrop"_attr = indexNamesToDrop); // Report the ready indexes. @@ -453,8 +425,7 @@ StatusWith<BSONObj> IndexCatalogImpl::prepareSpecForCreate( } // First check against only the ready indexes for conflicts. - status = - _doesSpecConflictWithExisting(opCtx, collection, validatedSpec, InclusionPolicy::kReady); + status = _doesSpecConflictWithExisting(opCtx, collection, validatedSpec, false); if (!status.isOK()) { return status; } @@ -470,12 +441,7 @@ StatusWith<BSONObj> IndexCatalogImpl::prepareSpecForCreate( // The index catalog cannot currently iterate over only in-progress indexes. So by previously // checking against only ready indexes without error, we know that any errors encountered // checking against all indexes occurred due to an in-progress index. - status = _doesSpecConflictWithExisting(opCtx, - collection, - validatedSpec, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished | - IndexCatalog::InclusionPolicy::kFrozen); + status = _doesSpecConflictWithExisting(opCtx, collection, validatedSpec, true); if (!status.isOK()) { if (ErrorCodes::IndexAlreadyExists == status.code()) { // Callers need to be able to distinguish conflicts against ready indexes versus @@ -504,11 +470,8 @@ std::vector<BSONObj> IndexCatalogImpl::removeExistingIndexesNoChecks( // _doesSpecConflictWithExisting currently does more work than we require here: we are only // interested in the index already exists error. if (ErrorCodes::IndexAlreadyExists == - _doesSpecConflictWithExisting(opCtx, - collection, - spec, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished)) { + _doesSpecConflictWithExisting( + opCtx, collection, spec, true /*includeUnfinishedIndexes*/)) { continue; } @@ -571,20 +534,19 @@ IndexCatalogEntry* IndexCatalogImpl::createIndexEntry(OperationContext* opCtx, engine->getEngine()->alterIdentMetadata(opCtx, ident, desc, isForceUpdateMetadata); } - if (!frozen) { - const auto& collOptions = collection->getCollectionOptions(); - std::unique_ptr<SortedDataInterface> sdi = engine->getEngine()->getSortedDataInterface( - opCtx, collection->ns(), collOptions, ident, desc); - std::unique_ptr<IndexAccessMethod> accessMethod = - IndexAccessMethod::make(entry.get(), std::move(sdi)); - entry->setAccessMethod(std::move(accessMethod)); - } + const auto& collOptions = collection->getCollectionOptions(); + std::unique_ptr<SortedDataInterface> sdi = engine->getEngine()->getSortedDataInterface( + opCtx, collection->ns(), collOptions, ident, desc); + + std::unique_ptr<IndexAccessMethod> accessMethod = + IndexAccessMethodFactory::get(opCtx)->make(entry.get(), std::move(sdi)); + + entry->init(std::move(accessMethod)); + IndexCatalogEntry* save = entry.get(); if (isReadyIndex) { _readyIndexes.add(std::move(entry)); - } else if (frozen) { - _frozenIndexes.add(std::move(entry)); } else { _buildingIndexes.add(std::move(entry)); } @@ -623,7 +585,7 @@ StatusWith<BSONObj> IndexCatalogImpl::createIndexOnEmptyCollection(OperationCont boost::optional<UUID> buildUUID = boost::none; IndexBuildBlock indexBuildBlock( collection->ns(), spec, IndexBuildMethod::kForeground, buildUUID); - status = indexBuildBlock.init(opCtx, collection, /*forRecovery=*/false); + status = indexBuildBlock.init(opCtx, collection); if (!status.isOK()) return status; @@ -911,11 +873,10 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, } const std::unique_ptr<MatchExpression> filterExpr = std::move(statusWithMatcher.getValue()); - Status status = _checkValidFilterExpressions( - filterExpr.get(), - !serverGlobalParams.featureCompatibility.isVersionInitialized() || - feature_flags::gTimeseriesMetricIndexes.isEnabled( - serverGlobalParams.featureCompatibility)); + Status status = + _checkValidFilterExpressions(filterExpr.get(), + feature_flags::gTimeseriesMetricIndexes.isEnabled( + serverGlobalParams.featureCompatibility)); if (!status.isOK()) { return status; } @@ -990,7 +951,7 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, const CollectionPtr& collection, const BSONObj& spec, - InclusionPolicy inclusionPolicy) const { + const bool includeUnfinishedIndexes) const { StringData name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); invariant(name[0]); @@ -1004,7 +965,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, { // Check whether an index with the specified candidate name already exists in the catalog. - const IndexDescriptor* desc = findIndexByName(opCtx, name, inclusionPolicy); + const IndexDescriptor* desc = findIndexByName(opCtx, name, includeUnfinishedIndexes); if (desc) { // Index already exists with same name. Check whether the options are the same as well. @@ -1057,7 +1018,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, { // No index with the candidate name exists. Check for an index with conflicting options. const IndexDescriptor* desc = - findIndexByKeyPatternAndOptions(opCtx, key, spec, inclusionPolicy); + findIndexByKeyPatternAndOptions(opCtx, key, spec, includeUnfinishedIndexes); if (desc) { LOGV2_DEBUG(20353, @@ -1108,7 +1069,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, string pluginName = IndexNames::findPluginName(key); if (pluginName == IndexNames::TEXT) { vector<const IndexDescriptor*> textIndexes; - findIndexByType(opCtx, IndexNames::TEXT, textIndexes, inclusionPolicy); + findIndexByType(opCtx, IndexNames::TEXT, textIndexes, includeUnfinishedIndexes); if (textIndexes.size() > 0) { return Status(ErrorCodes::CannotCreateIndex, str::stream() << "only one text index per collection allowed, " @@ -1149,10 +1110,7 @@ void IndexCatalogImpl::dropIndexes(OperationContext* opCtx, vector<string> indexNamesToDrop; { int seen = 0; - auto ii = getIndexIterator(opCtx, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished | - IndexCatalog::InclusionPolicy::kFrozen); + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, true); while (ii->more()) { seen++; const IndexDescriptor* desc = ii->next()->descriptor(); @@ -1167,11 +1125,7 @@ void IndexCatalogImpl::dropIndexes(OperationContext* opCtx, for (size_t i = 0; i < indexNamesToDrop.size(); i++) { string indexName = indexNamesToDrop[i]; - const IndexDescriptor* desc = findIndexByName( - opCtx, - indexName, - IndexCatalog::InclusionPolicy::kReady | IndexCatalog::InclusionPolicy::kUnfinished | - IndexCatalog::InclusionPolicy::kFrozen); + const IndexDescriptor* desc = findIndexByName(opCtx, indexName, true); invariant(desc); LOGV2_DEBUG(20355, 1, "\t dropAllIndexes dropping: {desc}", "desc"_attr = *desc); IndexCatalogEntry* entry = desc->getEntry(); @@ -1230,73 +1184,6 @@ Status IndexCatalogImpl::dropIndex(OperationContext* opCtx, return dropIndexEntry(opCtx, collection, entry); } -Status IndexCatalogImpl::resetUnfinishedIndexForRecovery(OperationContext* opCtx, - Collection* collection, - const IndexDescriptor* desc) { - invariant(opCtx->lockState()->isCollectionLockedForMode(collection->ns(), MODE_X)); - invariant(opCtx->lockState()->inAWriteUnitOfWork()); - - IndexCatalogEntry* entry = desc->getEntry(); - const std::string indexName = entry->descriptor()->indexName(); - - // Only indexes that aren't ready can be reset. - invariant(!collection->isIndexReady(indexName)); - - auto released = [&] { - if (auto released = _readyIndexes.release(entry->descriptor())) { - invariant(!released, "Cannot reset a ready index"); - } - if (auto released = _buildingIndexes.release(entry->descriptor())) { - return released; - } - if (auto released = _frozenIndexes.release(entry->descriptor())) { - return released; - } - MONGO_UNREACHABLE; - }(); - - LOGV2(6987700, - "Resetting unfinished index", - logAttrs(collection->ns()), - "index"_attr = indexName, - "ident"_attr = released->getIdent()); - - invariant(released.get() == entry); - - // Drop the ident if it exists. The storage engine will return OK if the ident is not found. - auto engine = opCtx->getServiceContext()->getStorageEngine(); - const std::string ident = released->getIdent(); - Status status = engine->getEngine()->dropIdent(opCtx->recoveryUnit(), ident); - if (!status.isOK()) { - return status; - } - - // Recreate the ident on-disk. DurableCatalog::createIndex() will lookup the ident internally - // using the catalogId and index name. - status = DurableCatalog::get(opCtx)->createIndex(opCtx, - collection->getCatalogId(), - collection->ns(), - collection->getCollectionOptions(), - released->descriptor()); - if (!status.isOK()) { - return status; - } - - // Update the index entry state in preparation to rebuild the index. - if (!released->accessMethod()) { - std::unique_ptr<SortedDataInterface> sdi = engine->getEngine()->getSortedDataInterface( - opCtx, collection->ns(), collection->getCollectionOptions(), ident, desc); - std::unique_ptr<IndexAccessMethod> accessMethod = - IndexAccessMethod::make(released.get(), std::move(sdi)); - released->setAccessMethod(std::move(accessMethod)); - } - - released->setIsFrozen(false); - _buildingIndexes.add(std::move(released)); - - return Status::OK(); -} - Status IndexCatalogImpl::dropUnfinishedIndex(OperationContext* opCtx, Collection* collection, const IndexDescriptor* desc) { @@ -1364,26 +1251,25 @@ Status IndexCatalogImpl::dropIndexEntry(OperationContext* opCtx, audit::logDropIndex(opCtx->getClient(), indexName, collection->ns()); - auto released = [&] { - if (auto released = _readyIndexes.release(entry->descriptor())) { - return released; - } - if (auto released = _buildingIndexes.release(entry->descriptor())) { - return released; - } - if (auto released = _frozenIndexes.release(entry->descriptor())) { - return released; - } - MONGO_UNREACHABLE; - }(); - - invariant(released.get() == entry); - opCtx->recoveryUnit()->registerChange( - std::make_unique<IndexRemoveChange>(opCtx, - collection->ns(), - collection->uuid(), - std::move(released), - collection->getSharedDecorations())); + auto released = _readyIndexes.release(entry->descriptor()); + if (released) { + invariant(released.get() == entry); + opCtx->recoveryUnit()->registerChange( + std::make_unique<IndexRemoveChange>(opCtx, + collection->ns(), + collection->uuid(), + std::move(released), + collection->getSharedDecorations())); + } else { + released = _buildingIndexes.release(entry->descriptor()); + invariant(released.get() == entry); + opCtx->recoveryUnit()->registerChange( + std::make_unique<IndexRemoveChange>(opCtx, + collection->ns(), + collection->uuid(), + std::move(released), + collection->getSharedDecorations())); + } CollectionQueryInfo::get(collection).rebuildIndexData(opCtx, collection); CollectionIndexUsageTrackerDecoration::get(collection->getSharedDecorations()) @@ -1403,11 +1289,7 @@ void IndexCatalogImpl::_deleteIndexFromDisk(OperationContext* opCtx, Collection* collection, const string& indexName, std::shared_ptr<Ident> ident) { - invariant(!findIndexByName(opCtx, - indexName, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished | - IndexCatalog::InclusionPolicy::kFrozen)); + invariant(!findIndexByName(opCtx, indexName, true /* includeUnfinishedIndexes*/)); catalog::removeIndex(opCtx, indexName, collection, std::move(ident)); } @@ -1437,7 +1319,7 @@ int IndexCatalogImpl::numIndexesTotal(OperationContext* opCtx) const { int IndexCatalogImpl::numIndexesReady(OperationContext* opCtx) const { std::vector<const IndexDescriptor*> itIndexes; - auto ii = getIndexIterator(opCtx, InclusionPolicy::kReady); + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, /*includeUnfinished*/ false); while (ii->more()) { itIndexes.push_back(ii->next()->descriptor()); } @@ -1449,7 +1331,7 @@ bool IndexCatalogImpl::haveIdIndex(OperationContext* opCtx) const { } const IndexDescriptor* IndexCatalogImpl::findIdIndex(OperationContext* opCtx) const { - auto ii = getIndexIterator(opCtx, InclusionPolicy::kReady); + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, false); while (ii->more()) { const IndexDescriptor* desc = ii->next()->descriptor(); if (desc->isIdIndex()) @@ -1460,8 +1342,8 @@ const IndexDescriptor* IndexCatalogImpl::findIdIndex(OperationContext* opCtx) co const IndexDescriptor* IndexCatalogImpl::findIndexByName(OperationContext* opCtx, StringData name, - InclusionPolicy inclusionPolicy) const { - auto ii = getIndexIterator(opCtx, inclusionPolicy); + bool includeUnfinishedIndexes) const { + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, includeUnfinishedIndexes); while (ii->more()) { const IndexDescriptor* desc = ii->next()->descriptor(); if (desc->indexName() == name) @@ -1474,8 +1356,8 @@ const IndexDescriptor* IndexCatalogImpl::findIndexByKeyPatternAndOptions( OperationContext* opCtx, const BSONObj& key, const BSONObj& indexSpec, - InclusionPolicy inclusionPolicy) const { - auto ii = getIndexIterator(opCtx, inclusionPolicy); + bool includeUnfinishedIndexes) const { + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, includeUnfinishedIndexes); IndexDescriptor needle(_getAccessMethodName(key), indexSpec); while (ii->more()) { const auto* entry = ii->next(); @@ -1489,10 +1371,10 @@ const IndexDescriptor* IndexCatalogImpl::findIndexByKeyPatternAndOptions( void IndexCatalogImpl::findIndexesByKeyPattern(OperationContext* opCtx, const BSONObj& key, - InclusionPolicy inclusionPolicy, + bool includeUnfinishedIndexes, std::vector<const IndexDescriptor*>* matches) const { invariant(matches); - auto ii = getIndexIterator(opCtx, inclusionPolicy); + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, includeUnfinishedIndexes); while (ii->more()) { const IndexDescriptor* desc = ii->next()->descriptor(); if (SimpleBSONObjComparator::kInstance.evaluate(desc->keyPattern() == key)) { @@ -1504,8 +1386,8 @@ void IndexCatalogImpl::findIndexesByKeyPattern(OperationContext* opCtx, void IndexCatalogImpl::findIndexByType(OperationContext* opCtx, const string& type, vector<const IndexDescriptor*>& matches, - InclusionPolicy inclusionPolicy) const { - auto ii = getIndexIterator(opCtx, inclusionPolicy); + bool includeUnfinishedIndexes) const { + std::unique_ptr<IndexIterator> ii = getIndexIterator(opCtx, includeUnfinishedIndexes); while (ii->more()) { const IndexDescriptor* desc = ii->next()->descriptor(); if (IndexNames::findPluginName(desc->keyPattern()) == type) { @@ -1746,10 +1628,7 @@ Status IndexCatalogImpl::indexRecords(OperationContext* opCtx, for (const MultikeyPathInfo& newPath : newPaths) { invariant(newPath.nss == coll->ns()); - auto idx = findIndexByName(opCtx, - newPath.indexName, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished); + auto idx = findIndexByName(opCtx, newPath.indexName, /*includeUnfinishedIndexes=*/true); if (!idx) { return Status(ErrorCodes::IndexNotFound, str::stream() @@ -1852,10 +1731,7 @@ Status IndexCatalogImpl::compactIndexes(OperationContext* opCtx) const { } std::string::size_type IndexCatalogImpl::getLongestIndexNameLength(OperationContext* opCtx) const { - auto it = getIndexIterator(opCtx, - IndexCatalog::InclusionPolicy::kReady | - IndexCatalog::InclusionPolicy::kUnfinished | - IndexCatalog::InclusionPolicy::kFrozen); + std::unique_ptr<IndexIterator> it = getIndexIterator(opCtx, true); std::string::size_type longestIndexNameLength = 0; while (it->more()) { auto thisLength = it->next()->descriptor()->indexName().length(); @@ -1903,11 +1779,7 @@ void IndexCatalogImpl::indexBuildSuccess(OperationContext* opCtx, invariant(releasedEntry.get() == index); _readyIndexes.add(std::move(releasedEntry)); - // Wait to unset the interceptor until the index actually commits. If a write conflict is - // encountered and the index commit process is restated, the multikey information from the - // interceptor may still be needed. - opCtx->recoveryUnit()->onCommit( - [index](boost::optional<Timestamp>) { index->setIndexBuildInterceptor(nullptr); }); + index->setIndexBuildInterceptor(nullptr); index->setIsReady(true); } |
