diff options
92 files changed, 1791 insertions, 1427 deletions
diff --git a/jstests/noPassthrough/profile_operation_metrics.js b/jstests/noPassthrough/profile_operation_metrics.js index 5f5e70742de..0c543aff7bb 100644 --- a/jstests/noPassthrough/profile_operation_metrics.js +++ b/jstests/noPassthrough/profile_operation_metrics.js @@ -418,8 +418,8 @@ const operations = [ profileFilter: {op: 'command', 'command.listIndexes': collName}, profileAssert: (db, profileDoc) => { // This reads from the collection catalog. - assert.gt(profileDoc.docBytesRead, 0); - assert.gt(profileDoc.docUnitsRead, 0); + assert.eq(profileDoc.docBytesRead, 0); + assert.eq(profileDoc.docUnitsRead, 0); assert.eq(profileDoc.idxEntryBytesRead, 0); assert.eq(profileDoc.idxEntryUnitsRead, 0); assert.eq(profileDoc.docBytesWritten, 0); diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index 0725840ba80..97d52070ac0 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -291,12 +291,14 @@ env.Library( source=[ 'collection_catalog.cpp', 'uncommitted_collections.cpp', + 'uncommitted_multikey.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/profile_filter', '$BUILD_DIR/mongo/db/service_context', + '$BUILD_DIR/mongo/db/storage/bson_collection_catalog_entry', '$BUILD_DIR/mongo/db/storage/snapshot_helper', 'collection', ], diff --git a/src/mongo/db/catalog/capped_utils.cpp b/src/mongo/db/catalog/capped_utils.cpp index ba86a56e539..a12247d6b69 100644 --- a/src/mongo/db/catalog/capped_utils.cpp +++ b/src/mongo/db/catalog/capped_utils.cpp @@ -51,7 +51,6 @@ #include "mongo/db/query/plan_yield_policy.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/views/view_catalog.h" #include "mongo/util/scopeguard.h" @@ -145,8 +144,7 @@ void cloneCollectionAsCapped(OperationContext* opCtx, // create new collection { - auto options = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, fromCollection->getCatalogId()); + auto options = fromCollection->getCollectionOptions(); // The capped collection will get its own new unique id, as the conversion isn't reversible, // so it can't be rolled back. options.uuid.reset(); diff --git a/src/mongo/db/catalog/capped_utils_test.cpp b/src/mongo/db/catalog/capped_utils_test.cpp index 50c8b634197..0ada277fc4f 100644 --- a/src/mongo/db/catalog/capped_utils_test.cpp +++ b/src/mongo/db/catalog/capped_utils_test.cpp @@ -38,7 +38,6 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_impl.h" #include "mongo/db/service_context_d_test_fixture.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/unittest.h" namespace { @@ -99,7 +98,7 @@ CollectionOptions getCollectionOptions(OperationContext* opCtx, const NamespaceS AutoGetCollectionForRead collection(opCtx, nss); ASSERT_TRUE(collection) << "Unable to get collections options for " << nss << " because collection does not exist."; - return DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + return collection->getCollectionOptions(); } // Size of capped collection to be passed to convertToCapped() which accepts a double. diff --git a/src/mongo/db/catalog/catalog_control.cpp b/src/mongo/db/catalog/catalog_control.cpp index 04200591d50..c5c1bb95f63 100644 --- a/src/mongo/db/catalog/catalog_control.cpp +++ b/src/mongo/db/catalog/catalog_control.cpp @@ -127,12 +127,12 @@ void openCatalog(OperationContext* opCtx, // Determine which indexes need to be rebuilt. rebuildIndexesOnCollection() requires that all // indexes on that collection are done at once, so we use a map to group them together. StringMap<IndexNameObjs> nsToIndexNameObjMap; + auto catalog = CollectionCatalog::get(opCtx); for (StorageEngine::IndexIdentifier indexIdentifier : reconcileResult.indexesToRebuild) { auto indexName = indexIdentifier.indexName; - auto indexSpecs = - getIndexNameObjs(opCtx, - indexIdentifier.catalogId, - [&indexName](const std::string& name) { return name == indexName; }); + auto coll = catalog->lookupCollectionByNamespace(opCtx, indexIdentifier.nss); + auto indexSpecs = getIndexNameObjs( + coll, [&indexName](const std::string& name) { return name == indexName; }); if (!indexSpecs.isOK() || indexSpecs.getValue().first.empty()) { fassert(40689, {ErrorCodes::InternalError, @@ -154,7 +154,6 @@ void openCatalog(OperationContext* opCtx, ino.second.emplace_back(std::move(indexesToRebuild.second.back())); } - auto catalog = CollectionCatalog::get(opCtx); for (const auto& entry : nsToIndexNameObjMap) { NamespaceString collNss(entry.first); diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp index a50aaf22edb..f0504822d73 100644 --- a/src/mongo/db/catalog/coll_mod.cpp +++ b/src/mongo/db/catalog/coll_mod.cpp @@ -55,7 +55,6 @@ #include "mongo/db/s/sharding_state.h" #include "mongo/db/server_options.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/recovery_unit.h" #include "mongo/db/ttl_collection_cache.h" #include "mongo/db/views/view_catalog.h" @@ -385,7 +384,7 @@ private: void _setClusteredExpireAfterSeconds(OperationContext* opCtx, const CollectionOptions& oldCollOptions, - const CollectionPtr& coll, + Collection* coll, const BSONElement& clusteredIndexExpireAfterSeconds) { invariant(oldCollOptions.clusteredIndex.has_value()); @@ -400,8 +399,7 @@ void _setClusteredExpireAfterSeconds(OperationContext* opCtx, return; } - DurableCatalog::get(opCtx)->updateClusteredIndexTTLSetting( - opCtx, coll->getCatalogId(), boost::none); + coll->updateClusteredIndexTTLSetting(opCtx, boost::none); return; } @@ -421,8 +419,7 @@ void _setClusteredExpireAfterSeconds(OperationContext* opCtx, } invariant(newExpireAfterSeconds >= 0); - DurableCatalog::get(opCtx)->updateClusteredIndexTTLSetting( - opCtx, coll->getCatalogId(), newExpireAfterSeconds); + coll->updateClusteredIndexTTLSetting(opCtx, newExpireAfterSeconds); } Status _collModInternal(OperationContext* opCtx, @@ -526,15 +523,16 @@ Status _collModInternal(OperationContext* opCtx, // options to provide to the OpObserver. TTL index updates aren't a part of collection // options so we save the relevant TTL index data in a separate object. - CollectionOptions oldCollOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, coll->getCatalogId()); + const CollectionOptions& oldCollOptions = coll->getCollectionOptions(); boost::optional<IndexCollModInfo> indexCollModInfo; // Handle collMod operation type appropriately. if (clusteredIndexExpireAfterSeconds) { - _setClusteredExpireAfterSeconds( - opCtx, oldCollOptions, coll.getCollection(), clusteredIndexExpireAfterSeconds); + _setClusteredExpireAfterSeconds(opCtx, + oldCollOptions, + coll.getWritableCollection(), + clusteredIndexExpireAfterSeconds); } if (indexExpireAfterSeconds || indexHidden) { @@ -550,10 +548,8 @@ Status _collModInternal(OperationContext* opCtx, if (SimpleBSONElementComparator::kInstance.evaluate(oldExpireSecs != newExpireSecs)) { // Change the value of "expireAfterSeconds" on disk. - DurableCatalog::get(opCtx)->updateTTLSetting(opCtx, - coll->getCatalogId(), - idx->indexName(), - newExpireSecs.safeNumberLong()); + coll.getWritableCollection()->updateTTLSetting( + opCtx, idx->indexName(), newExpireSecs.safeNumberLong()); } } @@ -564,8 +560,8 @@ Status _collModInternal(OperationContext* opCtx, // Make sure when we set 'hidden' to false, we can remove the hidden field from // catalog. if (SimpleBSONElementComparator::kInstance.evaluate(oldHidden != newHidden)) { - DurableCatalog::get(opCtx)->updateHiddenSetting( - opCtx, coll->getCatalogId(), idx->indexName(), newHidden.booleanSafe()); + coll.getWritableCollection()->updateHiddenSetting( + opCtx, idx->indexName(), newHidden.booleanSafe()); } } @@ -581,7 +577,8 @@ Status _collModInternal(OperationContext* opCtx, // Notify the index catalog that the definition of this index changed. This will // invalidate the local idx pointer. On rollback of this WUOW, the idx pointer in // cmrNew will be invalidated and the local var idx pointer will be valid again. - cmrNew.idx = coll.getWritableCollection()->getIndexCatalog()->refreshEntry(opCtx, idx); + cmrNew.idx = coll.getWritableCollection()->getIndexCatalog()->refreshEntry( + opCtx, coll.getWritableCollection(), idx); opCtx->recoveryUnit()->registerChange(std::make_unique<CollModResultChange>( oldExpireSecs, newExpireSecs, oldHidden, newHidden, result)); diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h index e95215b3832..9fdf42db69d 100644 --- a/src/mongo/db/catalog/collection.h +++ b/src/mongo/db/catalog/collection.h @@ -49,6 +49,7 @@ #include "mongo/db/query/plan_executor.h" #include "mongo/db/record_id.h" #include "mongo/db/repl/oplog.h" +#include "mongo/db/storage/bson_collection_catalog_entry.h" #include "mongo/db/storage/capped_callback.h" #include "mongo/db/storage/record_store.h" #include "mongo/db/storage/snapshot.h" @@ -194,6 +195,17 @@ public: RecordId catalogId, const CollectionOptions& options, std::unique_ptr<RecordStore> rs) const = 0; + + /** + * Constructs a Collection object. This does not persist any state to the storage engine, + * only constructs an in-memory representation of what already exists on disk. + */ + virtual std::shared_ptr<Collection> make( + OperationContext* opCtx, + const NamespaceString& nss, + RecordId catalogId, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> metadata, + std::unique_ptr<RecordStore> rs) const = 0; }; /** @@ -288,11 +300,11 @@ public: /** * Sets a new namespace on this Collection, in the case that the Collection is being renamed. * In general, reads and writes to Collection objects are synchronized using locks from the lock - * manager. However, there is special synchronization for ns() and setNs() so that the + * manager. However, there is special synchronization for ns() and rename() so that the * CollectionCatalog can perform UUID to namespace lookup without holding a Collection lock. See - * CollectionCatalog::setCollectionNamespace(). + * CollectionCatalog::onCollectionRename(). */ - virtual void setNs(NamespaceString nss) = 0; + virtual Status rename(OperationContext* opCtx, const NamespaceString& nss, bool stayTemp) = 0; virtual RecordId getCatalogId() const = 0; @@ -499,11 +511,8 @@ public: /** * Returns true if this is a temporary collection. - * - * Calling this function is somewhat costly because it requires accessing the storage engine's - * cache of collection information. */ - virtual bool isTemporary(OperationContext* opCtx) const = 0; + virtual bool isTemporary() const = 0; /** * Returns true if this collection is clustered on _id values. That is, its RecordIds are _id @@ -511,9 +520,128 @@ public: */ virtual bool isClustered() const = 0; + /** + * Updates the expireAfterSeconds setting for a clustered TTL index in this Collection and the + * durable catalog. + */ + virtual void updateClusteredIndexTTLSetting(OperationContext* opCtx, + boost::optional<int64_t> expireAfterSeconds) = 0; + virtual Status updateCappedSize(OperationContext* opCtx, long long newCappedSize) = 0; // + // Index + // + + /** + * Checks that the metadata for the index exists and matches the given spec. + */ + virtual Status checkMetaDataForIndex(const std::string& indexName, + const BSONObj& spec) const = 0; + + /* + * Updates the expireAfterSeconds field of the given index to the value in newExpireSecs. + * The specified index must already contain an expireAfterSeconds field, and the value in + * that field and newExpireSecs must both be numeric. + */ + virtual void updateTTLSetting(OperationContext* opCtx, + StringData idxName, + long long newExpireSeconds) = 0; + + /* + * Hide or unhide the given index. A hidden index will not be considered for use by the + * query planner. + */ + virtual void updateHiddenSetting(OperationContext* opCtx, StringData idxName, bool hidden) = 0; + + /** + * Updates the 'temp' setting for this collection. + */ + virtual void setIsTemp(OperationContext* opCtx, bool isTemp) = 0; + + /** + * Removes the index 'indexName' from the persisted collection catalog entry identified by + * 'catalogId'. + */ + virtual void removeIndex(OperationContext* opCtx, StringData indexName) = 0; + + /** + * Updates the persisted catalog entry for 'ns' with the new index and creates the index on + * disk. + * + * A passed 'buildUUID' implies that the index is part of a two-phase index build. + */ + virtual Status prepareForIndexBuild(OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild) = 0; + + /** + * Returns a UUID if the index is being built with the two-phase index build procedure. + */ + virtual boost::optional<UUID> getIndexBuildUUID(StringData indexName) const = 0; + + /** + * Returns true if the index identified by 'indexName' is multikey, and returns false otherwise. + * + * If the 'multikeyPaths' pointer is non-null, then it must point to an empty vector. If this + * index type supports tracking path-level multikey information in the catalog, then this + * function sets 'multikeyPaths' as the path components that cause this index to be multikey. + * + * In particular, if this function returns false and the index supports tracking path-level + * multikey information, then 'multikeyPaths' is initialized as a vector with size equal to the + * number of elements in the index key pattern of empty sets. + */ + virtual bool isIndexMultikey(OperationContext* opCtx, + StringData indexName, + MultikeyPaths* multikeyPaths) const = 0; + + /** + * Sets the index identified by 'indexName' to be multikey. + * + * If 'multikeyPaths' is non-empty, then it must be a vector with size equal to the number of + * elements in the index key pattern. Additionally, at least one path component of the indexed + * fields must cause this index to be multikey. + * + * This function returns true if the index metadata has changed, and returns false otherwise. + */ + virtual bool setIndexIsMultikey(OperationContext* opCtx, + StringData indexName, + const MultikeyPaths& multikeyPaths) const = 0; + + /** + * Sets the index to be multikey with the provided paths. This performs minimal validation of + * the inputs and is intended to be used internally to "correct" multikey metadata that drifts + * from the underlying collection data. + * + * When isMultikey is false, ignores multikeyPaths and resets the metadata appropriately based + * on the index descriptor. Otherwise, overwrites the existing multikeyPaths with the ones + * provided. This only writes multikey paths if the index type supports path-level tracking, and + * only sets the multikey boolean flag otherwise. + */ + virtual void forceSetIndexIsMultikey(OperationContext* opCtx, + const IndexDescriptor* desc, + bool isMultikey, + const MultikeyPaths& multikeyPaths) const = 0; + + virtual int getTotalIndexCount() const = 0; + + virtual int getCompletedIndexCount() const = 0; + + virtual BSONObj getIndexSpec(StringData indexName) const = 0; + + virtual void getAllIndexes(std::vector<std::string>* names) const = 0; + + virtual void getReadyIndexes(std::vector<std::string>* names) const = 0; + + virtual bool isIndexPresent(StringData indexName) const = 0; + + virtual bool isIndexReady(StringData indexName) const = 0; + + virtual void replaceMetadata(OperationContext* opCtx, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> md) = 0; + + // // Stats // @@ -578,6 +706,12 @@ public: virtual const CollatorInterface* getDefaultCollator() const = 0; /** + * Returns a cached version of the Collection MetaData that matches the version of this + * Collection instance. + */ + virtual const CollectionOptions& getCollectionOptions() const = 0; + + /** * Fills in each index specification with collation information from this collection and returns * the new index specifications. * diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp index 397bef7086f..6b99d3d9afb 100644 --- a/src/mongo/db/catalog/collection_catalog.cpp +++ b/src/mongo/db/catalog/collection_catalog.cpp @@ -536,16 +536,10 @@ void CollectionCatalog::write(OperationContext* opCtx, } -void CollectionCatalog::setCollectionNamespace(OperationContext* opCtx, - Collection* coll, - const NamespaceString& fromCollection, - const NamespaceString& toCollection) const { - // Rather than maintain, in addition to the UUID -> Collection* mapping, an auxiliary - // data structure with the UUID -> namespace mapping, the CollectionCatalog relies on - // Collection::ns() to provide UUID to namespace lookup. In addition, the CollectionCatalog - // does not require callers to hold locks. +void CollectionCatalog::onCollectionRename(OperationContext* opCtx, + Collection* coll, + const NamespaceString& fromCollection) const { invariant(coll); - coll->setNs(toCollection); auto& uncommittedCatalogUpdates = getUncommittedCatalogUpdates(opCtx); uncommittedCatalogUpdates.rename(coll, fromCollection); @@ -563,18 +557,6 @@ void CollectionCatalog::dropCollection(OperationContext* opCtx, Collection* coll PublishCatalogUpdates::ensureRegisteredWithRecoveryUnit(opCtx, uncommittedCatalogUpdates); } -void CollectionCatalog::dropCollection(OperationContext* opCtx, const CollectionPtr& coll) const { - invariant(coll); - invariant(opCtx->lockState()->isCollectionLockedForMode(coll->ns(), MODE_X)); - - auto& uncommittedCatalogUpdates = getUncommittedCatalogUpdates(opCtx); - uncommittedCatalogUpdates.drop(coll.get()); - - // Ensure we have registered publish change if this collection haven't been made writable - // previously - PublishCatalogUpdates::ensureRegisteredWithRecoveryUnit(opCtx, uncommittedCatalogUpdates); -} - void CollectionCatalog::onCloseDatabase(OperationContext* opCtx, std::string dbName) { invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_X)); auto rid = ResourceId(RESOURCE_DATABASE, dbName); diff --git a/src/mongo/db/catalog/collection_catalog.h b/src/mongo/db/catalog/collection_catalog.h index 00a80372821..ff5fda03fdf 100644 --- a/src/mongo/db/catalog/collection_catalog.h +++ b/src/mongo/db/catalog/collection_catalog.h @@ -150,16 +150,14 @@ public: static void write(OperationContext* opCtx, CatalogWriteFn job); /** - * This function is responsible for safely setting the namespace string inside 'coll' to the - * value of 'toCollection'. The caller need not hold locks on the collection. + * This function is responsible for safely tracking a Collection rename within a + * WriteUnitOfWork. * - * Must be called within a WriteUnitOfWork. The Collection namespace will be set back to - * 'fromCollection' if the WriteUnitOfWork aborts. + * Must be called within a WriteUnitOfWork. */ - void setCollectionNamespace(OperationContext* opCtx, - Collection* coll, - const NamespaceString& fromCollection, - const NamespaceString& toCollection) const; + void onCollectionRename(OperationContext* opCtx, + Collection* coll, + const NamespaceString& fromCollection) const; /** * Marks a collection as dropped for this OperationContext. Will cause the collection @@ -169,7 +167,6 @@ public: * Must be called within a WriteUnitOfWork. */ void dropCollection(OperationContext* opCtx, Collection* coll) const; - void dropCollection(OperationContext* opCtx, const CollectionPtr& coll) const; void onCloseDatabase(OperationContext* opCtx, std::string dbName); diff --git a/src/mongo/db/catalog/collection_catalog_test.cpp b/src/mongo/db/catalog/collection_catalog_test.cpp index 07a7a0e4602..9bee62814aa 100644 --- a/src/mongo/db/catalog/collection_catalog_test.cpp +++ b/src/mongo/db/catalog/collection_catalog_test.cpp @@ -37,7 +37,6 @@ #include "mongo/db/concurrency/lock_manager_defs.h" #include "mongo/db/operation_context_noop.h" #include "mongo/db/service_context_d_test_fixture.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" @@ -444,7 +443,7 @@ TEST_F(CollectionCatalogTest, RenameCollection) { ASSERT_EQUALS(catalog.lookupCollectionByUUID(&opCtx, uuid), collection); NamespaceString newNss(nss.db(), "newcol"); - collection->setNs(newNss); + ASSERT_OK(collection->rename(&opCtx, newNss, false)); ASSERT_EQ(collection->ns(), newNss); ASSERT_EQUALS(catalog.lookupCollectionByUUID(&opCtx, uuid), collection); } @@ -704,9 +703,7 @@ TEST_F(ForEachCollectionFromDbTest, ForEachCollectionFromDbWithPredicate) { [&](const CollectionPtr& collection) { ASSERT_TRUE( opCtx->lockState()->isCollectionLockedForMode(collection->ns(), MODE_NONE)); - return DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .temp; + return collection->getCollectionOptions().temp; }); ASSERT_EQUALS(numCollectionsTraversed, 2); @@ -728,9 +725,7 @@ TEST_F(ForEachCollectionFromDbTest, ForEachCollectionFromDbWithPredicate) { [&](const CollectionPtr& collection) { ASSERT_TRUE( opCtx->lockState()->isCollectionLockedForMode(collection->ns(), MODE_NONE)); - return !DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .temp; + return !collection->getCollectionOptions().temp; }); ASSERT_EQUALS(numCollectionsTraversed, 1); diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp index 7232e02c390..a7f84f1007c 100644 --- a/src/mongo/db/catalog/collection_impl.cpp +++ b/src/mongo/db/catalog/collection_impl.cpp @@ -45,6 +45,7 @@ #include "mongo/db/catalog/index_catalog_impl.h" #include "mongo/db/catalog/index_consistency.h" #include "mongo/db/catalog/index_key_validate.h" +#include "mongo/db/catalog/uncommitted_multikey.h" #include "mongo/db/clientcursor.h" #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/concurrency/d_concurrency.h" @@ -237,6 +238,10 @@ private: WriteUnitOfWork::RecoveryUnitState _originalRecoveryUnitState; }; +bool indexTypeSupportsPathLevelMultikeyTracking(StringData accessMethod) { + return accessMethod == IndexNames::BTREE || accessMethod == IndexNames::GEO_2DSPHERE; +} + } // namespace CollectionImpl::SharedState::SharedState(CollectionImpl* collection, @@ -248,8 +253,7 @@ CollectionImpl::SharedState::SharedState(CollectionImpl* collection, : nullptr), _needCappedLock(options.capped && collection->ns().db() != "local"), _isCapped(options.capped), - _cappedMaxDocs(options.cappedMaxDocs), - _cappedMaxSize(options.cappedSize) { + _cappedMaxDocs(options.cappedMaxDocs) { if (_cappedNotifier) { _recordStore->setCappedCallback(this); } @@ -292,7 +296,16 @@ CollectionImpl::CollectionImpl(OperationContext* opCtx, _catalogId(catalogId), _uuid(options.uuid.get()), _shared(std::make_shared<SharedState>(this, std::move(recordStore), options)), - _indexCatalog(std::make_unique<IndexCatalogImpl>(this)) {} + _indexCatalog(std::make_unique<IndexCatalogImpl>()) {} + +CollectionImpl::CollectionImpl(OperationContext* opCtx, + const NamespaceString& nss, + RecordId catalogId, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> metadata, + std::unique_ptr<RecordStore> recordStore) + : CollectionImpl(opCtx, nss, catalogId, metadata->options, std::move(recordStore)) { + _metadata = std::move(metadata); +} CollectionImpl::~CollectionImpl() { _shared->instanceDeleted(this); @@ -313,9 +326,18 @@ std::shared_ptr<Collection> CollectionImpl::FactoryImpl::make( return std::make_shared<CollectionImpl>(opCtx, nss, catalogId, options, std::move(rs)); } +std::shared_ptr<Collection> CollectionImpl::FactoryImpl::make( + OperationContext* opCtx, + const NamespaceString& nss, + RecordId catalogId, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> metadata, + std::unique_ptr<RecordStore> rs) const { + return std::make_shared<CollectionImpl>( + opCtx, nss, catalogId, std::move(metadata), std::move(rs)); +} + std::shared_ptr<Collection> CollectionImpl::clone() const { auto cloned = std::make_shared<CollectionImpl>(*this); - checked_cast<IndexCatalogImpl*>(cloned->_indexCatalog.get())->setCollection(cloned.get()); cloned->_shared->instanceCreated(cloned.get()); // We are per definition committed if we get cloned cloned->_cachedCommitted = true; @@ -327,8 +349,9 @@ SharedCollectionDecorations* CollectionImpl::getSharedDecorations() const { } void CollectionImpl::init(OperationContext* opCtx) { - auto collectionOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, getCatalogId()); + _metadata = DurableCatalog::get(opCtx)->getMetaData(opCtx, getCatalogId()); + const auto& collectionOptions = _metadata->options; + _shared->_collator = parseCollation(opCtx, _ns, collectionOptions.collation); auto validatorDoc = collectionOptions.validator.getOwned(); @@ -337,11 +360,8 @@ void CollectionImpl::init(OperationContext* opCtx) { // Make sure to copy the action and level before parsing MatchExpression, since certain features // are not supported with certain combinations of action and level. - _validationAction = collectionOptions.validationAction; - _validationLevel = collectionOptions.validationLevel; if (collectionOptions.recordPreImages) { uassertStatusOK(validatePreImageRecording(opCtx, _ns)); - _recordPreImages = true; } // Store the result (OK / error) of parsing the validator, but do not enforce that the result is @@ -383,7 +403,7 @@ void CollectionImpl::init(OperationContext* opCtx) { } } - getIndexCatalog()->init(opCtx).transitional_ignore(); + getIndexCatalog()->init(opCtx, this).transitional_ignore(); _initialized = true; } @@ -472,7 +492,7 @@ Status CollectionImpl::checkValidation(OperationContext* opCtx, const BSONObj& d if (!validatorMatchExpr) return Status::OK(); - if (validationLevelOrDefault(_validationLevel) == ValidationLevelEnum::off) + if (validationLevelOrDefault(_metadata->options.validationLevel) == ValidationLevelEnum::off) return Status::OK(); if (DocumentValidationSettings::get(opCtx).isSchemaValidationDisabled()) @@ -505,7 +525,8 @@ Status CollectionImpl::checkValidation(OperationContext* opCtx, const BSONObj& d // writes which result in the validator throwing an exception are accepted when we're in // warn mode. if (!isFCVAtLeast47 && - validationActionOrDefault(_validationAction) == ValidationActionEnum::error) { + validationActionOrDefault(_metadata->options.validationAction) == + ValidationActionEnum::error) { e.addContext("Document validation failed"); throw; } @@ -516,7 +537,8 @@ Status CollectionImpl::checkValidation(OperationContext* opCtx, const BSONObj& d generatedError = doc_validation_error::generateError(*validatorMatchExpr, document); } - if (validationActionOrDefault(_validationAction) == ValidationActionEnum::warn) { + if (validationActionOrDefault(_metadata->options.validationAction) == + ValidationActionEnum::warn) { LOGV2_WARNING(20294, "Document would fail validation", "namespace"_attr = ns(), @@ -568,8 +590,10 @@ Collection::Validator CollectionImpl::parseValidator( // If the validation action is "warn" or the level is "moderate", then disallow any encryption // keywords. This is to prevent any plaintext data from showing up in the logs. - if (validationActionOrDefault(_validationAction) == ValidationActionEnum::warn || - validationLevelOrDefault(_validationLevel) == ValidationLevelEnum::moderate) + if (validationActionOrDefault(_metadata->options.validationAction) == + ValidationActionEnum::warn || + validationLevelOrDefault(_metadata->options.validationLevel) == + ValidationLevelEnum::moderate) allowedFeatures &= ~MatchExpressionParser::AllowedFeatures::kEncryptKeywords; auto statusWithMatcher = @@ -846,7 +870,7 @@ bool CollectionImpl::_cappedAndNeedDelete(OperationContext* opCtx) const { return false; } - if (dataSize(opCtx) > _shared->_cappedMaxSize) { + if (dataSize(opCtx) > _shared->_collectionLatest->getCollectionOptions().cappedSize) { return true; } @@ -921,8 +945,9 @@ void CollectionImpl::_cappedDeleteAsNeeded(OperationContext* opCtx, const long long currentDataSize = dataSize(opCtx); const long long currentNumRecords = numRecords(opCtx); + const auto cappedMaxSize = _shared->_collectionLatest->getCollectionOptions().cappedSize; const long long sizeOverCap = - (currentDataSize > _shared->_cappedMaxSize) ? currentDataSize - _shared->_cappedMaxSize : 0; + (currentDataSize > cappedMaxSize) ? currentDataSize - cappedMaxSize : 0; const long long docsOverCap = (_shared->_cappedMaxDocs != 0 && currentNumRecords > _shared->_cappedMaxDocs) ? currentNumRecords - _shared->_cappedMaxDocs @@ -978,8 +1003,12 @@ void CollectionImpl::_cappedDeleteAsNeeded(OperationContext* opCtx, } int64_t unusedKeysDeleted = 0; - _indexCatalog->unindexRecord( - opCtx, doc, record->id, /*logIfError=*/false, &unusedKeysDeleted); + _indexCatalog->unindexRecord(opCtx, + CollectionPtr(this, CollectionPtr::NoYieldTag{}), + doc, + record->id, + /*logIfError=*/false, + &unusedKeysDeleted); // We're about to delete the record our cursor is positioned on, so advance the cursor. RecordId toDelete = record->id; @@ -1040,7 +1069,8 @@ Status CollectionImpl::SharedState::aboutToDeleteCapped(OperationContext* opCtx, RecordData data) { BSONObj doc = data.releaseToBson(); int64_t* const nullKeysDeleted = nullptr; - _collectionLatest->getIndexCatalog()->unindexRecord(opCtx, doc, loc, false, nullKeysDeleted); + _collectionLatest->getIndexCatalog()->unindexRecord( + opCtx, _collectionLatest, doc, loc, false, nullKeysDeleted); // We are not capturing and reporting to OpDebug the 'keysDeleted' by unindexRecord(). It is // questionable whether reporting will add diagnostic value to users and may instead be @@ -1086,7 +1116,12 @@ void CollectionImpl::deleteDocument(OperationContext* opCtx, } int64_t keysDeleted; - _indexCatalog->unindexRecord(opCtx, doc.value(), loc, noWarn, &keysDeleted); + _indexCatalog->unindexRecord(opCtx, + CollectionPtr(this, CollectionPtr::NoYieldTag{}), + doc.value(), + loc, + noWarn, + &keysDeleted); _shared->_recordStore->deleteRecord(opCtx, loc); OpObserver::OplogDeleteEntryArgs deleteArgs{nullptr, fromMigrate, getRecordPreImages()}; @@ -1113,7 +1148,8 @@ RecordId CollectionImpl::updateDocument(OperationContext* opCtx, { auto status = checkValidation(opCtx, newDoc); if (!status.isOK()) { - if (validationLevelOrDefault(_validationLevel) == ValidationLevelEnum::strict) { + if (validationLevelOrDefault(_metadata->options.validationLevel) == + ValidationLevelEnum::strict) { uassertStatusOK(status); } // moderate means we have to check the old doc @@ -1232,14 +1268,25 @@ StatusWith<RecordData> CollectionImpl::updateDocumentWithDamages( return newRecStatus; } -bool CollectionImpl::isTemporary(OperationContext* opCtx) const { - return DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, getCatalogId()).temp; +bool CollectionImpl::isTemporary() const { + return _metadata->options.temp; } bool CollectionImpl::isClustered() const { return _clustered; } +void CollectionImpl::updateClusteredIndexTTLSetting(OperationContext* opCtx, + boost::optional<int64_t> expireAfterSeconds) { + uassert(5401000, + "The collection doesn't have a clustered index", + _metadata->options.clusteredIndex); + + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.options.clusteredIndex->setExpireAfterSeconds(expireAfterSeconds); + }); +} + Status CollectionImpl::updateCappedSize(OperationContext* opCtx, long long newCappedSize) { invariant(opCtx->lockState()->isCollectionLockedForMode(ns(), MODE_X)); @@ -1255,20 +1302,23 @@ Status CollectionImpl::updateCappedSize(OperationContext* opCtx, long long newCa } } - _shared->_cappedMaxSize = newCappedSize; + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.options.cappedSize = newCappedSize; + }); return Status::OK(); } bool CollectionImpl::getRecordPreImages() const { - return _recordPreImages; + return _metadata->options.recordPreImages; } void CollectionImpl::setRecordPreImages(OperationContext* opCtx, bool val) { if (val) { uassertStatusOK(validatePreImageRecording(opCtx, _ns)); } - DurableCatalog::get(opCtx)->setRecordPreImages(opCtx, getCatalogId(), val); - _recordPreImages = val; + + _writeMetadata( + opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { md.options.recordPreImages = val; }); } bool CollectionImpl::isCapped() const { @@ -1280,7 +1330,7 @@ long long CollectionImpl::getCappedMaxDocs() const { } long long CollectionImpl::getCappedMaxSize() const { - return _shared->_cappedMaxSize; + return _metadata->options.cappedSize; } CappedCallback* CollectionImpl::getCappedCallback() { @@ -1390,7 +1440,7 @@ Status CollectionImpl::truncate(OperationContext* opCtx) { } // 2) drop indexes - _indexCatalog->dropAllIndexes(opCtx, true); + _indexCatalog->dropAllIndexes(opCtx, this, true); // 3) truncate record store auto status = _shared->_recordStore->truncate(opCtx); @@ -1399,7 +1449,8 @@ Status CollectionImpl::truncate(OperationContext* opCtx) { // 4) re-create indexes for (size_t i = 0; i < indexSpecs.size(); i++) { - status = _indexCatalog->createIndexOnEmptyCollection(opCtx, indexSpecs[i]).getStatus(); + status = + _indexCatalog->createIndexOnEmptyCollection(opCtx, this, indexSpecs[i]).getStatus(); if (!status.isOK()) return status; } @@ -1420,32 +1471,36 @@ void CollectionImpl::cappedTruncateAfter(OperationContext* opCtx, void CollectionImpl::setValidator(OperationContext* opCtx, Validator validator) { invariant(opCtx->lockState()->isCollectionLockedForMode(ns(), MODE_X)); - DurableCatalog::get(opCtx)->updateValidator(opCtx, - getCatalogId(), - validator.validatorDoc.getOwned(), - validationLevelOrDefault(_validationLevel), - validationActionOrDefault(_validationAction)); + auto validatorDoc = validator.validatorDoc.getOwned(); + auto validationLevel = validationLevelOrDefault(_metadata->options.validationLevel); + auto validationAction = validationActionOrDefault(_metadata->options.validationAction); + + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.options.validator = validatorDoc; + md.options.validationLevel = validationLevel; + md.options.validationAction = validationAction; + }); _validator = std::move(validator); } boost::optional<ValidationLevelEnum> CollectionImpl::getValidationLevel() const { - return _validationLevel; + return _metadata->options.validationLevel; } boost::optional<ValidationActionEnum> CollectionImpl::getValidationAction() const { - return _validationAction; + return _metadata->options.validationAction; } Status CollectionImpl::setValidationLevel(OperationContext* opCtx, ValidationLevelEnum newLevel) { invariant(opCtx->lockState()->isCollectionLockedForMode(ns(), MODE_X)); - _validationLevel = newLevel; + auto storedValidationLevel = validationLevelOrDefault(newLevel); // Reparse the validator as there are some features which are only supported with certain // validation levels. auto allowedFeatures = MatchExpressionParser::kAllowAllSpecialFeatures; - if (validationLevelOrDefault(_validationLevel) == ValidationLevelEnum::moderate) + if (storedValidationLevel == ValidationLevelEnum::moderate) allowedFeatures &= ~MatchExpressionParser::AllowedFeatures::kEncryptKeywords; _validator = parseValidator(opCtx, _validator.validatorDoc, allowedFeatures); @@ -1453,11 +1508,9 @@ Status CollectionImpl::setValidationLevel(OperationContext* opCtx, ValidationLev return _validator.getStatus(); } - DurableCatalog::get(opCtx)->updateValidator(opCtx, - getCatalogId(), - _validator.validatorDoc, - validationLevelOrDefault(_validationLevel), - validationActionOrDefault(_validationAction)); + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.options.validationLevel = storedValidationLevel; + }); return Status::OK(); } @@ -1466,12 +1519,12 @@ Status CollectionImpl::setValidationAction(OperationContext* opCtx, ValidationActionEnum newAction) { invariant(opCtx->lockState()->isCollectionLockedForMode(ns(), MODE_X)); - _validationAction = newAction; + auto storedValidationAction = validationActionOrDefault(newAction); // Reparse the validator as there are some features which are only supported with certain // validation actions. auto allowedFeatures = MatchExpressionParser::kAllowAllSpecialFeatures; - if (validationActionOrDefault(_validationAction) == ValidationActionEnum::warn) + if (storedValidationAction == ValidationActionEnum::warn) allowedFeatures &= ~MatchExpressionParser::AllowedFeatures::kEncryptKeywords; _validator = parseValidator(opCtx, _validator.validatorDoc, allowedFeatures); @@ -1479,11 +1532,9 @@ Status CollectionImpl::setValidationAction(OperationContext* opCtx, return _validator.getStatus(); } - DurableCatalog::get(opCtx)->updateValidator(opCtx, - getCatalogId(), - _validator.validatorDoc, - validationLevelOrDefault(_validationLevel), - validationActionOrDefault(_validationAction)); + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.options.validationAction = storedValidationAction; + }); return Status::OK(); } @@ -1494,17 +1545,19 @@ Status CollectionImpl::updateValidator(OperationContext* opCtx, boost::optional<ValidationActionEnum> newAction) { invariant(opCtx->lockState()->isCollectionLockedForMode(ns(), MODE_X)); - DurableCatalog::get(opCtx)->updateValidator( - opCtx, getCatalogId(), newValidator, newLevel, newAction); - auto validator = parseValidator(opCtx, newValidator, MatchExpressionParser::kAllowAllSpecialFeatures); if (!validator.isOK()) { return validator.getStatus(); } + + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.options.validator = newValidator; + md.options.validationLevel = newLevel; + md.options.validationAction = newAction; + }); + _validator = std::move(validator); - _validationLevel = newLevel; - _validationAction = newAction; return Status::OK(); } @@ -1516,6 +1569,10 @@ const CollatorInterface* CollectionImpl::getDefaultCollator() const { return _shared->_collator.get(); } +const CollectionOptions& CollectionImpl::getCollectionOptions() const { + return _metadata->options; +} + StatusWith<std::vector<BSONObj>> CollectionImpl::addCollationDefaultsToIndexSpecsForCreate( OperationContext* opCtx, const std::vector<BSONObj>& originalIndexSpecs) const { std::vector<BSONObj> newIndexSpecs; @@ -1575,14 +1632,35 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> CollectionImpl::makePlanExe opCtx, &yieldableCollection, yieldPolicy, direction, resumeAfterRecordId); } -void CollectionImpl::setNs(NamespaceString nss) { +Status CollectionImpl::rename(OperationContext* opCtx, const NamespaceString& nss, bool stayTemp) { + auto metadata = std::make_shared<BSONCollectionCatalogEntry::MetaData>(*_metadata); + metadata->ns = nss.ns(); + if (!stayTemp) + metadata->options.temp = false; + Status status = + DurableCatalog::get(opCtx)->renameCollection(opCtx, getCatalogId(), nss, *metadata); + if (!status.isOK()) { + return status; + } + + _metadata = std::move(metadata); _ns = std::move(nss); _shared->_recordStore.get()->setNs(_ns); + return status; } void CollectionImpl::indexBuildSuccess(OperationContext* opCtx, IndexCatalogEntry* index) { - DurableCatalog::get(opCtx)->indexBuildSuccess( - opCtx, getCatalogId(), index->descriptor()->indexName()); + const auto& indexName = index->descriptor()->indexName(); + int offset = _metadata->findIndexOffset(indexName); + invariant(offset >= 0, + str::stream() << "cannot mark index " << indexName << " as ready @ " << getCatalogId() + << " : " << _metadata->toBSON()); + + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.indexes[offset].ready = true; + md.indexes[offset].buildUUID = boost::none; + }); + _indexCatalog->indexBuildSuccess(opCtx, this, index); } @@ -1590,4 +1668,342 @@ void CollectionImpl::establishOplogCollectionForLogging(OperationContext* opCtx) repl::establishOplogCollectionForLogging(opCtx, this); } +Status CollectionImpl::checkMetaDataForIndex(const std::string& indexName, + const BSONObj& spec) const { + int offset = _metadata->findIndexOffset(indexName); + if (offset < 0) { + return {ErrorCodes::IndexNotFound, + str::stream() << "Index [" << indexName + << "] not found in metadata for recordId: " << getCatalogId()}; + } + + if (spec.woCompare(_metadata->indexes[offset].spec)) { + return {ErrorCodes::BadValue, + str::stream() << "Spec for index [" << indexName + << "] does not match spec in the metadata for recordId: " + << getCatalogId() << ". Spec: " << spec + << " metadata's spec: " << _metadata->indexes[offset].spec}; + } + + return Status::OK(); +} + +void CollectionImpl::updateTTLSetting(OperationContext* opCtx, + StringData idxName, + long long newExpireSeconds) { + int offset = _metadata->findIndexOffset(idxName); + invariant(offset >= 0, + str::stream() << "cannot update TTL setting for index " << idxName << " @ " + << getCatalogId() << " : " << _metadata->toBSON()); + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.indexes[offset].updateTTLSetting(newExpireSeconds); + }); +} + +void CollectionImpl::updateHiddenSetting(OperationContext* opCtx, StringData idxName, bool hidden) { + int offset = _metadata->findIndexOffset(idxName); + invariant(offset >= 0); + + _writeMetadata(opCtx, [&](BSONCollectionCatalogEntry::MetaData& md) { + md.indexes[offset].updateHiddenSetting(hidden); + }); +} + +void CollectionImpl::setIsTemp(OperationContext* opCtx, bool isTemp) { + _writeMetadata(opCtx, + [&](BSONCollectionCatalogEntry::MetaData& md) { md.options.temp = isTemp; }); +} + +void CollectionImpl::removeIndex(OperationContext* opCtx, StringData indexName) { + if (_metadata->findIndexOffset(indexName) < 0) + return; // never had the index so nothing to do. + + _writeMetadata(opCtx, + [&](BSONCollectionCatalogEntry::MetaData& md) { md.eraseIndex(indexName); }); +} + +Status CollectionImpl::prepareForIndexBuild(OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild) { + + auto durableCatalog = DurableCatalog::get(opCtx); + auto imd = durableCatalog->prepareIndexMetaDataForIndexBuild( + opCtx, spec, buildUUID, isBackgroundSecondaryBuild); + + // Confirm that our index is not already in the current metadata. + invariant(-1 == _metadata->findIndexOffset(imd.name())); + + _writeMetadata(opCtx, + [&](BSONCollectionCatalogEntry::MetaData& md) { md.indexes.push_back(imd); }); + + return durableCatalog->createIndex(opCtx, getCatalogId(), getCollectionOptions(), spec); +} + +boost::optional<UUID> CollectionImpl::getIndexBuildUUID(StringData indexName) const { + int offset = _metadata->findIndexOffset(indexName); + invariant(offset >= 0, + str::stream() << "cannot get build UUID for index " << indexName << " @ " + << getCatalogId() << " : " << _metadata->toBSON()); + return _metadata->indexes[offset].buildUUID; +} + +bool CollectionImpl::isIndexMultikey(OperationContext* opCtx, + StringData indexName, + MultikeyPaths* multikeyPaths) const { + auto isMultikey = + [this, multikeyPaths, indexName](const BSONCollectionCatalogEntry::MetaData& metadata) { + int offset = metadata.findIndexOffset(indexName); + invariant(offset >= 0, + str::stream() << "cannot get multikey for index " << indexName << " @ " + << getCatalogId() << " : " << metadata.toBSON()); + + const auto& index = metadata.indexes[offset]; + stdx::lock_guard lock(index.multikeyMutex); + if (multikeyPaths && !index.multikeyPaths.empty()) { + *multikeyPaths = index.multikeyPaths; + } + + return index.multikey; + }; + + const auto& uncommittedMultikeys = UncommittedMultikey::get(opCtx).resources(); + if (uncommittedMultikeys) { + if (auto it = uncommittedMultikeys->find(this); it != uncommittedMultikeys->end()) { + return isMultikey(it->second); + } + } + + return isMultikey(*_metadata); +} + +bool CollectionImpl::setIndexIsMultikey(OperationContext* opCtx, + StringData indexName, + const MultikeyPaths& multikeyPaths) const { + + auto setMultikey = [this, name = indexName.toString(), multikeyPaths]( + const BSONCollectionCatalogEntry::MetaData& metadata) { + int offset = metadata.findIndexOffset(name); + invariant(offset >= 0, + str::stream() << "cannot set index " << name << " as multikey @ " + << getCatalogId() << " : " << metadata.toBSON()); + + { + const auto& index = metadata.indexes[offset]; + stdx::lock_guard lock(index.multikeyMutex); + + const bool tracksPathLevelMultikeyInfo = + !metadata.indexes[offset].multikeyPaths.empty(); + if (tracksPathLevelMultikeyInfo) { + invariant(!multikeyPaths.empty()); + invariant(multikeyPaths.size() == metadata.indexes[offset].multikeyPaths.size()); + } else { + invariant(multikeyPaths.empty()); + + if (metadata.indexes[offset].multikey) { + // The index is already set as multikey and we aren't tracking path-level + // multikey information for it. We return false to indicate that the index + // metadata is unchanged. + return false; + } + } + + index.multikey = true; + + if (tracksPathLevelMultikeyInfo) { + bool newPathIsMultikey = false; + bool somePathIsMultikey = false; + + // Store new path components that cause this index to be multikey in catalog's + // index metadata. + for (size_t i = 0; i < multikeyPaths.size(); ++i) { + MultikeyComponents& indexMultikeyComponents = index.multikeyPaths[i]; + for (const auto multikeyComponent : multikeyPaths[i]) { + auto result = indexMultikeyComponents.insert(multikeyComponent); + newPathIsMultikey = newPathIsMultikey || result.second; + somePathIsMultikey = true; + } + } + + // If all of the sets in the multikey paths vector were empty, then no component + // of any indexed field caused the index to be multikey. setIndexIsMultikey() + // therefore shouldn't have been called. + invariant(somePathIsMultikey); + + if (!newPathIsMultikey) { + // We return false to indicate that the index metadata is unchanged. + return false; + } + } + } + return true; + }; + + // Make a copy that is safe to read without locks that we insert in the durable catalog, we only + // update the stored metadata on successful commit. The pending update is stored as a decoration + // on the OperationContext to allow us to read our own writes. + auto& uncommittedMultikeys = UncommittedMultikey::get(opCtx).resources(); + if (!uncommittedMultikeys) { + uncommittedMultikeys = std::make_shared<UncommittedMultikey::MultikeyMap>(); + } + BSONCollectionCatalogEntry::MetaData* metadata = nullptr; + if (auto it = uncommittedMultikeys->find(this); it != uncommittedMultikeys->end()) { + metadata = &it->second; + } else { + metadata = &uncommittedMultikeys->emplace(this, *_metadata).first->second; + } + + if (!setMultikey(*metadata)) + return false; + DurableCatalog::get(opCtx)->putMetaData(opCtx, getCatalogId(), *metadata); + + opCtx->recoveryUnit()->onCommit( + [this, uncommittedMultikeys, setMultikey = std::move(setMultikey)](auto ts) { + // Merge in changes to this index, other indexes may have been updated since we made our + // copy. Don't check for result as another thread could be setting multikey at the same + // time + setMultikey(*_metadata); + uncommittedMultikeys->erase(this); + }); + opCtx->recoveryUnit()->onRollback( + [this, uncommittedMultikeys]() { uncommittedMultikeys->erase(this); }); + return true; +} + +void CollectionImpl::forceSetIndexIsMultikey(OperationContext* opCtx, + const IndexDescriptor* desc, + bool isMultikey, + const MultikeyPaths& multikeyPaths) const { + auto forceSetMultikey = [this, + isMultikey, + indexName = desc->indexName(), + accessMethod = desc->getAccessMethodName(), + numKeyPatternFields = desc->keyPattern().nFields(), + multikeyPaths](const BSONCollectionCatalogEntry::MetaData& metadata) { + int offset = metadata.findIndexOffset(indexName); + invariant(offset >= 0, + str::stream() << "cannot set index " << indexName << " multikey state @ " + << getCatalogId() << " : " << metadata.toBSON()); + + const auto& index = metadata.indexes[offset]; + stdx::lock_guard lock(index.multikeyMutex); + index.multikey = isMultikey; + if (indexTypeSupportsPathLevelMultikeyTracking(accessMethod)) { + if (isMultikey) { + index.multikeyPaths = multikeyPaths; + } else { + index.multikeyPaths = MultikeyPaths{static_cast<size_t>(numKeyPatternFields)}; + } + } + }; + + // Make a copy that is safe to read without locks that we insert in the durable catalog, we only + // update the stored metadata on successful commit. The pending update is stored as a decoration + // on the OperationContext to allow us to read our own writes. + auto& uncommittedMultikeys = UncommittedMultikey::get(opCtx).resources(); + if (!uncommittedMultikeys) { + uncommittedMultikeys = std::make_shared<UncommittedMultikey::MultikeyMap>(); + } + BSONCollectionCatalogEntry::MetaData* metadata = nullptr; + if (auto it = uncommittedMultikeys->find(this); it != uncommittedMultikeys->end()) { + metadata = &it->second; + } else { + metadata = &uncommittedMultikeys->emplace(this, *_metadata).first->second; + } + forceSetMultikey(*metadata); + DurableCatalog::get(opCtx)->putMetaData(opCtx, getCatalogId(), *metadata); + opCtx->recoveryUnit()->onCommit( + [this, uncommittedMultikeys, forceSetMultikey = std::move(forceSetMultikey)](auto ts) { + // Merge in changes to this index, other indexes may have been updated since we made our + // copy. + forceSetMultikey(*_metadata); + uncommittedMultikeys->erase(this); + }); + opCtx->recoveryUnit()->onRollback( + [this, uncommittedMultikeys]() { uncommittedMultikeys->erase(this); }); +} + +int CollectionImpl::getTotalIndexCount() const { + return static_cast<int>(_metadata->indexes.size()); +} + +int CollectionImpl::getCompletedIndexCount() const { + int num = 0; + for (unsigned i = 0; i < _metadata->indexes.size(); i++) { + if (_metadata->indexes[i].ready) + num++; + } + return num; +} + +BSONObj CollectionImpl::getIndexSpec(StringData indexName) const { + int offset = _metadata->findIndexOffset(indexName); + invariant(offset >= 0, + str::stream() << "cannot get index spec for " << indexName << " @ " << getCatalogId() + << " : " << _metadata->toBSON()); + + return _metadata->indexes[offset].spec; +} + +void CollectionImpl::getAllIndexes(std::vector<std::string>* names) const { + for (unsigned i = 0; i < _metadata->indexes.size(); i++) { + names->push_back(_metadata->indexes[i].spec["name"].String()); + } +} + +void CollectionImpl::getReadyIndexes(std::vector<std::string>* names) const { + for (unsigned i = 0; i < _metadata->indexes.size(); i++) { + if (_metadata->indexes[i].ready) + names->push_back(_metadata->indexes[i].spec["name"].String()); + } +} + +bool CollectionImpl::isIndexPresent(StringData indexName) const { + int offset = _metadata->findIndexOffset(indexName); + return offset >= 0; +} + +bool CollectionImpl::isIndexReady(StringData indexName) const { + int offset = _metadata->findIndexOffset(indexName); + invariant(offset >= 0, + str::stream() << "cannot get ready status for index " << indexName << " @ " + << getCatalogId() << " : " << _metadata->toBSON()); + return _metadata->indexes[offset].ready; +} + +void CollectionImpl::replaceMetadata(OperationContext* opCtx, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> md) { + DurableCatalog::get(opCtx)->putMetaData(opCtx, getCatalogId(), *md); + _metadata = std::move(md); +} + +template <typename Func> +void CollectionImpl::_writeMetadata(OperationContext* opCtx, Func func) { + // Even though we are holding an exclusive lock on the Collection there may be an ongoing + // multikey change on this OperationContext. Make sure we include that update when we copy the + // metadata for this operation. + const BSONCollectionCatalogEntry::MetaData* sourceMetadata = _metadata.get(); + auto& uncommittedMultikeys = UncommittedMultikey::get(opCtx).resources(); + if (uncommittedMultikeys) { + if (auto it = uncommittedMultikeys->find(this); it != uncommittedMultikeys->end()) { + sourceMetadata = &it->second; + } + } + + // Copy metadata and apply provided function to make change. + auto metadata = std::make_shared<BSONCollectionCatalogEntry::MetaData>(*sourceMetadata); + func(*metadata); + + // Remove the cached multikey change, it is now included in the copied metadata. If we left it + // here we could read stale data. + if (uncommittedMultikeys) { + uncommittedMultikeys->erase(this); + } + + // Store in durable catalog and replace pointer with our copied instance. + DurableCatalog::get(opCtx)->putMetaData(opCtx, getCatalogId(), *metadata); + _metadata = std::move(metadata); +} + + } // namespace mongo diff --git a/src/mongo/db/catalog/collection_impl.h b/src/mongo/db/catalog/collection_impl.h index 9571407a98b..b6a17849603 100644 --- a/src/mongo/db/catalog/collection_impl.h +++ b/src/mongo/db/catalog/collection_impl.h @@ -41,23 +41,38 @@ class CollectionCatalog; class CollectionImpl final : public Collection { public: + // TODO SERVER-56999: We should just need one API to create Collections explicit CollectionImpl(OperationContext* opCtx, const NamespaceString& nss, RecordId catalogId, const CollectionOptions& options, std::unique_ptr<RecordStore> recordStore); + explicit CollectionImpl(OperationContext* opCtx, + const NamespaceString& nss, + RecordId catalogId, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> metadata, + std::unique_ptr<RecordStore> recordStore); + ~CollectionImpl(); std::shared_ptr<Collection> clone() const final; class FactoryImpl : public Factory { public: + // TODO SERVER-56999: We should just need one API to create Collections std::shared_ptr<Collection> make(OperationContext* opCtx, const NamespaceString& nss, RecordId catalogId, const CollectionOptions& options, std::unique_ptr<RecordStore> rs) const final; + + std::shared_ptr<Collection> make( + OperationContext* opCtx, + const NamespaceString& nss, + RecordId catalogId, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> metadata, + std::unique_ptr<RecordStore> rs) const final; }; SharedCollectionDecorations* getSharedDecorations() const final; @@ -71,7 +86,7 @@ public: return _ns; } - void setNs(NamespaceString nss) final; + Status rename(OperationContext* opCtx, const NamespaceString& nss, bool stayTemp) final; RecordId getCatalogId() const { return _catalogId; @@ -298,9 +313,11 @@ public: bool getRecordPreImages() const final; void setRecordPreImages(OperationContext* opCtx, bool val) final; - bool isTemporary(OperationContext* opCtx) const final; + bool isTemporary() const final; bool isClustered() const final; + void updateClusteredIndexTTLSetting(OperationContext* opCtx, + boost::optional<int64_t> expireAfterSeconds) final; Status updateCappedSize(OperationContext* opCtx, long long newCappedSize) final; @@ -373,6 +390,8 @@ public: */ const CollatorInterface* getDefaultCollator() const final; + const CollectionOptions& getCollectionOptions() const final; + StatusWith<std::vector<BSONObj>> addCollationDefaultsToIndexSpecsForCreate( OperationContext* opCtx, const std::vector<BSONObj>& indexSpecs) const final; @@ -388,6 +407,55 @@ public: void establishOplogCollectionForLogging(OperationContext* opCtx) final; void onDeregisterFromCatalog(OperationContext* opCtx) final; + Status checkMetaDataForIndex(const std::string& indexName, const BSONObj& spec) const final; + + void updateTTLSetting(OperationContext* opCtx, + StringData idxName, + long long newExpireSeconds) final; + + void updateHiddenSetting(OperationContext* opCtx, StringData idxName, bool hidden) final; + + void setIsTemp(OperationContext* opCtx, bool isTemp) final; + + void removeIndex(OperationContext* opCtx, StringData indexName) final; + + Status prepareForIndexBuild(OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild) final; + + boost::optional<UUID> getIndexBuildUUID(StringData indexName) const final; + + bool isIndexMultikey(OperationContext* opCtx, + StringData indexName, + MultikeyPaths* multikeyPaths) const final; + + bool setIndexIsMultikey(OperationContext* opCtx, + StringData indexName, + const MultikeyPaths& multikeyPaths) const final; + + void forceSetIndexIsMultikey(OperationContext* opCtx, + const IndexDescriptor* desc, + bool isMultikey, + const MultikeyPaths& multikeyPaths) const final; + + int getTotalIndexCount() const final; + + int getCompletedIndexCount() const final; + + BSONObj getIndexSpec(StringData indexName) const final; + + void getAllIndexes(std::vector<std::string>* names) const final; + + void getReadyIndexes(std::vector<std::string>* names) const final; + + bool isIndexPresent(StringData indexName) const final; + + bool isIndexReady(StringData indexName) const final; + + void replaceMetadata(OperationContext* opCtx, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> md) final; + private: /** * Returns a non-ok Status if document does not pass this collection's validator. @@ -414,6 +482,13 @@ private: void _cappedDeleteAsNeeded(OperationContext* opCtx, const RecordId& justInserted) const; /** + * Writes metadata to the DurableCatalog. Func should have the function signature + * 'void(BSONCollectionCatalogEntry::MetaData&)' + */ + template <typename Func> + void _writeMetadata(OperationContext* opCtx, Func func); + + /** * Holder of shared state between CollectionImpl clones. Also implements CappedCallback, a * pointer to which is given to the RecordStore, so that the CappedCallback logic can always be * performed on the latest CollectionImpl instance without needing to know about copy-on-write @@ -475,7 +550,6 @@ private: // Capped information. const bool _isCapped; const long long _cappedMaxDocs; - long long _cappedMaxSize; // For capped deletes performed on collections where '_needCappedLock' is false, the mutex // below protects '_cappedFirstRecord'. Otherwise, when '_needCappedLock' is true, the @@ -491,13 +565,15 @@ private: bool _cachedCommitted = true; std::shared_ptr<SharedState> _shared; + // Collection metadata cached from the DurableCatalog. Is kept separate from the SharedState + // because it may be updated. + std::shared_ptr<const BSONCollectionCatalogEntry::MetaData> _metadata; + clonable_ptr<IndexCatalog> _indexCatalog; // The validator is using shared state internally. Collections share validator until a new // validator is set in setValidator which sets a new instance. Validator _validator; - boost::optional<ValidationActionEnum> _validationAction; - boost::optional<ValidationLevelEnum> _validationLevel; // Whether or not this collection is clustered on _id values. bool _clustered = false; @@ -505,8 +581,6 @@ private: // If this is a time-series buckets collection, the metadata for this collection. boost::optional<TimeseriesOptions> _timeseriesOptions; - bool _recordPreImages = false; - // The earliest snapshot that is allowed to use this collection. boost::optional<Timestamp> _minVisibleSnapshot; diff --git a/src/mongo/db/catalog/collection_mock.h b/src/mongo/db/catalog/collection_mock.h index 244ecd571a9..7dfb7424494 100644 --- a/src/mongo/db/catalog/collection_mock.h +++ b/src/mongo/db/catalog/collection_mock.h @@ -73,8 +73,9 @@ public: return _ns; } - void setNs(NamespaceString nss) final { + Status rename(OperationContext* opCtx, const NamespaceString& nss, bool stayTemp) final { _ns = std::move(nss); + return Status::OK(); } const IndexCatalog* getIndexCatalog() const { @@ -228,7 +229,7 @@ public: std::abort(); } - bool isTemporary(OperationContext* opCtx) const { + bool isTemporary() const { std::abort(); } @@ -236,6 +237,11 @@ public: std::abort(); } + void updateClusteredIndexTTLSetting(OperationContext* opCtx, + boost::optional<int64_t> expireAfterSeconds) { + std::abort(); + } + Status updateCappedSize(OperationContext* opCtx, long long newCappedSize) { std::abort(); } @@ -311,6 +317,10 @@ public: std::abort(); } + const CollectionOptions& getCollectionOptions() const { + std::abort(); + } + StatusWith<std::vector<BSONObj>> addCollationDefaultsToIndexSpecsForCreate( OperationContext* opCtx, const std::vector<BSONObj>& indexSpecs) const { std::abort(); @@ -347,6 +357,89 @@ public: std::abort(); } + Status checkMetaDataForIndex(const std::string& indexName, const BSONObj& spec) const { + std::abort(); + } + + void updateTTLSetting(OperationContext* opCtx, StringData idxName, long long newExpireSeconds) { + std::abort(); + } + + void updateHiddenSetting(OperationContext* opCtx, StringData idxName, bool hidden) { + std::abort(); + } + + void setIsTemp(OperationContext* opCtx, bool isTemp) { + std::abort(); + } + + void removeIndex(OperationContext* opCtx, StringData indexName) { + std::abort(); + } + + Status prepareForIndexBuild(OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild) { + std::abort(); + } + + boost::optional<UUID> getIndexBuildUUID(StringData indexName) const { + std::abort(); + } + + bool isIndexMultikey(OperationContext* opCtx, + StringData indexName, + MultikeyPaths* multikeyPaths) const { + std::abort(); + } + + bool setIndexIsMultikey(OperationContext* opCtx, + StringData indexName, + const MultikeyPaths& multikeyPaths) const { + std::abort(); + } + + void forceSetIndexIsMultikey(OperationContext* opCtx, + const IndexDescriptor* desc, + bool isMultikey, + const MultikeyPaths& multikeyPaths) const final { + std::abort(); + } + + int getTotalIndexCount() const { + std::abort(); + } + + int getCompletedIndexCount() const { + std::abort(); + } + + BSONObj getIndexSpec(StringData indexName) const { + std::abort(); + } + + void getAllIndexes(std::vector<std::string>* names) const { + std::abort(); + } + + void getReadyIndexes(std::vector<std::string>* names) const { + std::abort(); + } + + bool isIndexPresent(StringData indexName) const { + std::abort(); + } + + bool isIndexReady(StringData indexName) const { + std::abort(); + } + + void replaceMetadata(OperationContext* opCtx, + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> md) { + std::abort(); + } + private: UUID _uuid = UUID::gen(); NamespaceString _ns; diff --git a/src/mongo/db/catalog/collection_options.cpp b/src/mongo/db/catalog/collection_options.cpp index bd63a6af67d..a2149f9a0ba 100644 --- a/src/mongo/db/catalog/collection_options.cpp +++ b/src/mongo/db/catalog/collection_options.cpp @@ -312,14 +312,14 @@ CollectionOptions CollectionOptions::fromCreateCommand(const CreateCommand& cmd) return options; } -BSONObj CollectionOptions::toBSON() const { +BSONObj CollectionOptions::toBSON(bool includeUUID) const { BSONObjBuilder b; - appendBSON(&b); + appendBSON(&b, includeUUID); return b.obj(); } -void CollectionOptions::appendBSON(BSONObjBuilder* builder) const { - if (uuid) { +void CollectionOptions::appendBSON(BSONObjBuilder* builder, bool includeUUID) const { + if (uuid && includeUUID) { builder->appendElements(uuid->toBSON()); } diff --git a/src/mongo/db/catalog/collection_options.h b/src/mongo/db/catalog/collection_options.h index 1a4bf368ea8..a8dcf0d28df 100644 --- a/src/mongo/db/catalog/collection_options.h +++ b/src/mongo/db/catalog/collection_options.h @@ -91,8 +91,12 @@ struct CollectionOptions { */ static CollectionOptions fromCreateCommand(const CreateCommand& cmd); - void appendBSON(BSONObjBuilder* builder) const; - BSONObj toBSON() const; + /** + * Serialize to BSON. The 'includeUUID' parameter is used for the listCollections command to do + * special formatting for the uuid. + */ + void appendBSON(BSONObjBuilder* builder, bool includeUUID) const; + BSONObj toBSON(bool includeUUID = true) const; /** * Returns true if given options matches to this. diff --git a/src/mongo/db/catalog/collection_validation.cpp b/src/mongo/db/catalog/collection_validation.cpp index 241f9fe4f05..1d06c548670 100644 --- a/src/mongo/db/catalog/collection_validation.cpp +++ b/src/mongo/db/catalog/collection_validation.cpp @@ -42,7 +42,6 @@ #include "mongo/db/db_raii.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/operation_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/views/view_catalog.h" #include "mongo/logv2/log.h" #include "mongo/util/fail_point.h" @@ -371,8 +370,7 @@ void _validateCatalogEntry(OperationContext* opCtx, ValidateState* validateState, ValidateResults* results) { const auto& collection = validateState->getCollection(); - CollectionOptions options = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + const auto& options = collection->getCollectionOptions(); if (options.uuid) { addErrorIfUnequal(*(options.uuid), validateState->uuid(), "UUID", results); } else { @@ -408,11 +406,10 @@ void _validateCatalogEntry(OperationContext* opCtx, } std::vector<std::string> indexes; - DurableCatalog::get(opCtx)->getReadyIndexes(opCtx, collection->getCatalogId(), &indexes); + collection->getReadyIndexes(&indexes); for (auto& index : indexes) { MultikeyPaths multikeyPaths; - const bool isMultikey = DurableCatalog::get(opCtx)->isIndexMultikey( - opCtx, collection->getCatalogId(), index, &multikeyPaths); + const bool isMultikey = collection->isIndexMultikey(opCtx, index, &multikeyPaths); const bool hasMultiKeyPaths = std::any_of(multikeyPaths.begin(), multikeyPaths.end(), [](auto& pathSet) { return pathSet.size() > 0; }); diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index 5239a29876a..b9c532386bd 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -277,11 +277,8 @@ Status _createTimeseries(OperationContext* opCtx, // Compare CollectionOptions and eventual TTL index to see if this bucket collection // may be reused for this request. existingBucketCollectionIsCompatible = - DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, coll->getCatalogId()) - .matchesStorageOptions( - bucketsOptions, - CollatorFactoryInterface::get(opCtx->getServiceContext())); + coll->getCollectionOptions().matchesStorageOptions( + bucketsOptions, CollatorFactoryInterface::get(opCtx->getServiceContext())); if (expireAfterSeconds && !bucketsOptions.clusteredIndex) { auto indexDescriptor = coll->getIndexCatalog()->findIndexByName(opCtx, indexName, true); diff --git a/src/mongo/db/catalog/create_collection_test.cpp b/src/mongo/db/catalog/create_collection_test.cpp index e56a2fa58b0..1a12247c55d 100644 --- a/src/mongo/db/catalog/create_collection_test.cpp +++ b/src/mongo/db/catalog/create_collection_test.cpp @@ -41,7 +41,6 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_impl.h" #include "mongo/db/service_context_d_test_fixture.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/unittest.h" #include "mongo/util/uuid.h" @@ -128,7 +127,7 @@ CollectionOptions getCollectionOptions(OperationContext* opCtx, const NamespaceS AutoGetCollectionForRead collection(opCtx, nss); ASSERT_TRUE(collection) << "Unable to get collections options for " << nss << " because collection does not exist."; - return DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + return collection->getCollectionOptions(); } /** diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp index 5c1d10e527d..d9f59b98ae5 100644 --- a/src/mongo/db/catalog/database_impl.cpp +++ b/src/mongo/db/catalog/database_impl.cpp @@ -230,9 +230,7 @@ void DatabaseImpl::clearTmpCollections(OperationContext* opCtx) const { }; CollectionCatalog::CollectionInfoFn predicate = [&](const CollectionPtr& collection) { - return DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .temp; + return collection->getCollectionOptions().temp; }; catalog::forEachCollectionFromDb(opCtx, name(), MODE_X, callback, predicate); @@ -515,10 +513,9 @@ void DatabaseImpl::_dropCollectionIndexes(OperationContext* opCtx, invariant(_name == nss.db()); LOGV2_DEBUG( 20316, 1, "dropCollection: {namespace} - dropAllIndexes start", "namespace"_attr = nss); - collection->getIndexCatalog()->dropAllIndexes(opCtx, true); + collection->getIndexCatalog()->dropAllIndexes(opCtx, collection, true); - invariant(DurableCatalog::get(opCtx)->getTotalIndexCount(opCtx, collection->getCatalogId()) == - 0); + invariant(collection->getTotalIndexCount() == 0); LOGV2_DEBUG( 20317, 1, "dropCollection: {namespace} - dropAllIndexes done", "namespace"_attr = nss); } @@ -577,16 +574,15 @@ Status DatabaseImpl::renameCollection(OperationContext* opCtx, Top::get(opCtx->getServiceContext()).collectionDropped(fromNss); - Status status = DurableCatalog::get(opCtx)->renameCollection( - opCtx, collToRename->getCatalogId(), toNss, stayTemp); - // Set the namespace of 'collToRename' from within the CollectionCatalog. This is necessary - // because the CollectionCatalog mutex synchronizes concurrent access to the collection's - // namespace for callers that may not hold a collection lock. + // because the CollectionCatalog manages the necessary isolation for this Collection until the + // WUOW commits. auto writableCollection = collToRename.getWritableCollection(); + Status status = writableCollection->rename(opCtx, toNss, stayTemp); + if (!status.isOK()) + return status; - CollectionCatalog::get(opCtx)->setCollectionNamespace( - opCtx, writableCollection, fromNss, toNss); + CollectionCatalog::get(opCtx)->onCollectionRename(opCtx, writableCollection, fromNss); opCtx->recoveryUnit()->onCommit([writableCollection](boost::optional<Timestamp> commitTime) { // Ban reading from this collection on committed reads on snapshots before now. @@ -746,7 +742,9 @@ Collection* DatabaseImpl::createCollection(OperationContext* opCtx, // initialized, so use the unsafe fCV getter here. IndexCatalog* ic = collection->getIndexCatalog(); fullIdIndexSpec = uassertStatusOK(ic->createIndexOnEmptyCollection( - opCtx, !idIndex.isEmpty() ? idIndex : ic->getDefaultIdIndexSpec())); + opCtx, + collection, + !idIndex.isEmpty() ? idIndex : ic->getDefaultIdIndexSpec(collection))); } else { // autoIndexId: false is only allowed on unreplicated collections. uassert(50001, diff --git a/src/mongo/db/catalog/database_test.cpp b/src/mongo/db/catalog/database_test.cpp index dde410d6f24..e2d826fa05a 100644 --- a/src/mongo/db/catalog/database_test.cpp +++ b/src/mongo/db/catalog/database_test.cpp @@ -57,7 +57,6 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_mock.h" #include "mongo/db/service_context_d_test_fixture.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/unittest.h" #include "mongo/util/scopeguard.h" @@ -377,8 +376,7 @@ TEST_F(DatabaseTest, RenameCollectionPreservesUuidOfSourceCollectionAndUpdatesUu auto toCollection = catalog->lookupCollectionByNamespace(opCtx, toNss); ASSERT_TRUE(toCollection); - auto toCollectionOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, toCollection->getCatalogId()); + const auto& toCollectionOptions = toCollection->getCollectionOptions(); auto toUuid = toCollectionOptions.uuid; ASSERT_TRUE(toUuid); diff --git a/src/mongo/db/catalog/drop_indexes.cpp b/src/mongo/db/catalog/drop_indexes.cpp index b23c3120f2d..4af7afc812e 100644 --- a/src/mongo/db/catalog/drop_indexes.cpp +++ b/src/mongo/db/catalog/drop_indexes.cpp @@ -184,7 +184,7 @@ std::vector<UUID> abortIndexBuildByIndexNames(OperationContext* opCtx, * Drops single index given a descriptor. */ Status dropIndexByDescriptor(OperationContext* opCtx, - const CollectionPtr& collection, + Collection* collection, IndexCatalog* indexCatalog, const IndexDescriptor* desc) { if (desc->isIdIndex()) { @@ -195,14 +195,14 @@ Status dropIndexByDescriptor(OperationContext* opCtx, // exist in standalone mode. auto entry = indexCatalog->getEntry(desc); if (entry->isFrozen()) { - invariant(!entry->isReady(opCtx)); + invariant(!entry->isReady(opCtx, collection)); invariant(getReplSetMemberInStandaloneMode(opCtx->getServiceContext())); // Return here. No need to fall through to op observer on standalone. - return indexCatalog->dropUnfinishedIndex(opCtx, desc); + return indexCatalog->dropUnfinishedIndex(opCtx, collection, desc); } // Do not allow dropping unfinished indexes that are not frozen. - if (!entry->isReady(opCtx)) { + if (!entry->isReady(opCtx, collection)) { return Status(ErrorCodes::IndexNotFound, str::stream() << "can't drop unfinished index with name: " << desc->indexName()); @@ -214,7 +214,7 @@ Status dropIndexByDescriptor(OperationContext* opCtx, opCtx->getServiceContext()->getOpObserver()->onDropIndex( opCtx, collection->ns(), collection->uuid(), desc->indexName(), desc->infoObj()); - auto s = indexCatalog->dropIndex(opCtx, desc); + auto s = indexCatalog->dropIndex(opCtx, collection, desc); if (!s.isOK()) { return s; } @@ -255,7 +255,7 @@ void dropReadyIndexes(OperationContext* opCtx, IndexCatalog* indexCatalog = collection->getIndexCatalog(); if (indexNames.front() == "*") { indexCatalog->dropAllIndexes( - opCtx, false, [opCtx, collection](const IndexDescriptor* desc) { + opCtx, collection, false, [opCtx, collection](const IndexDescriptor* desc) { opCtx->getServiceContext()->getOpObserver()->onDropIndex(opCtx, collection->ns(), collection->uuid(), @@ -427,8 +427,8 @@ DropIndexesReply dropIndexes(OperationContext* opCtx, continue; } - uassertStatusOK( - dropIndexByDescriptor(opCtx, collection->getCollection(), indexCatalog, desc)); + uassertStatusOK(dropIndexByDescriptor( + opCtx, collection->getWritableCollection(), indexCatalog, desc)); } wuow.commit(); diff --git a/src/mongo/db/catalog/index_build_block.cpp b/src/mongo/db/catalog/index_build_block.cpp index e34c309340f..48a95c05af1 100644 --- a/src/mongo/db/catalog/index_build_block.cpp +++ b/src/mongo/db/catalog/index_build_block.cpp @@ -98,7 +98,7 @@ Status IndexBuildBlock::initForResume(OperationContext* opCtx, // A bulk cursor can only be opened on a fresh table, so we drop the table that was created // before shutdown and recreate it. auto status = DurableCatalog::get(opCtx)->dropAndRecreateIndexIdentForResume( - opCtx, collection->getCatalogId(), descriptor, indexCatalogEntry->getIdent()); + opCtx, collection->getCollectionOptions(), descriptor, indexCatalogEntry->getIdent()); if (!status.isOK()) return status; } @@ -145,16 +145,13 @@ Status IndexBuildBlock::init(OperationContext* opCtx, Collection* collection) { } // Setup on-disk structures. - Status status = DurableCatalog::get(opCtx)->prepareForIndexBuild(opCtx, - collection->getCatalogId(), - descriptor.get(), - _buildUUID, - isBackgroundSecondaryBuild); + Status status = collection->prepareForIndexBuild( + opCtx, descriptor.get(), _buildUUID, isBackgroundSecondaryBuild); if (!status.isOK()) return status; auto indexCatalogEntry = collection->getIndexCatalog()->createIndexEntry( - opCtx, std::move(descriptor), CreateIndexEntryFlags::kNone); + opCtx, collection, std::move(descriptor), CreateIndexEntryFlags::kNone); if (_method == IndexBuildMethod::kHybrid) { _indexBuildInterceptor = std::make_unique<IndexBuildInterceptor>(opCtx, indexCatalogEntry); @@ -184,8 +181,6 @@ void IndexBuildBlock::fail(OperationContext* opCtx, Collection* collection) { // Being in a WUOW means all timestamping responsibility can be pushed up to the caller. invariant(opCtx->lockState()->inAWriteUnitOfWork()); - invariant(opCtx->lockState()->isCollectionLockedForMode(_nss, MODE_X)); - // Audit that the index build is being aborted. audit::logCreateIndex(opCtx->getClient(), &_spec, @@ -196,12 +191,14 @@ void IndexBuildBlock::fail(OperationContext* opCtx, Collection* collection) { auto indexCatalogEntry = getEntry(opCtx, collection); if (indexCatalogEntry) { - invariant(collection->getIndexCatalog()->dropIndexEntry(opCtx, indexCatalogEntry).isOK()); + invariant(collection->getIndexCatalog() + ->dropIndexEntry(opCtx, collection, indexCatalogEntry) + .isOK()); if (_indexBuildInterceptor) { indexCatalogEntry->setIndexBuildInterceptor(nullptr); } } else { - collection->getIndexCatalog()->deleteIndexFromDisk(opCtx, _indexName); + collection->getIndexCatalog()->deleteIndexFromDisk(opCtx, collection, _indexName); } } diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h index ae0edad4c25..c07d386fec9 100644 --- a/src/mongo/db/catalog/index_catalog.h +++ b/src/mongo/db/catalog/index_catalog.h @@ -187,7 +187,7 @@ public: virtual std::unique_ptr<IndexCatalog> clone() const = 0; // Must be called before used. - virtual Status init(OperationContext* const opCtx) = 0; + virtual Status init(OperationContext* const opCtx, Collection* collection) = 0; // ---- accessors ----- @@ -206,7 +206,7 @@ public: /** * Returns the spec for the id index to create by default for this collection. */ - virtual BSONObj getDefaultIdIndexSpec() const = 0; + virtual BSONObj getDefaultIdIndexSpec(const CollectionPtr& collection) const = 0; virtual const IndexDescriptor* findIdIndex(OperationContext* const opCtx) const = 0; @@ -279,6 +279,7 @@ public: * on the collection. */ virtual const IndexDescriptor* refreshEntry(OperationContext* const opCtx, + Collection* collection, const IndexDescriptor* const oldDesc) = 0; /** @@ -314,6 +315,7 @@ public: */ virtual IndexCatalogEntry* createIndexEntry(OperationContext* opCtx, + Collection* collection, std::unique_ptr<IndexDescriptor> descriptor, CreateIndexEntryFlags flags) = 0; @@ -323,6 +325,7 @@ public: * of the created index, as it is stored in this index catalog. */ virtual StatusWith<BSONObj> createIndexOnEmptyCollection(OperationContext* const opCtx, + Collection* collection, const BSONObj spec) = 0; /** @@ -335,6 +338,7 @@ public: */ virtual StatusWith<BSONObj> prepareSpecForCreate( OperationContext* const opCtx, + const CollectionPtr& collection, const BSONObj& original, const boost::optional<ResumeIndexInfo>& resumeInfo) const = 0; @@ -354,6 +358,7 @@ public: */ virtual std::vector<BSONObj> removeExistingIndexes( OperationContext* const opCtx, + const CollectionPtr& collection, const std::vector<BSONObj>& indexSpecsToBuild, const bool removeIndexBuildsToo) const = 0; @@ -368,7 +373,9 @@ public: * via replica set cloning or chunk migrations. */ virtual std::vector<BSONObj> removeExistingIndexesNoChecks( - OperationContext* const opCtx, const std::vector<BSONObj>& indexSpecsToBuild) const = 0; + OperationContext* const opCtx, + const CollectionPtr& collection, + const std::vector<BSONObj>& indexSpecsToBuild) const = 0; /** * Drops all indexes in the index catalog, optionally dropping the id index depending on the @@ -376,9 +383,12 @@ public: * index is dropped to allow timestamping each individual drop. */ virtual void dropAllIndexes(OperationContext* opCtx, + Collection* collection, bool includingIdIndex, std::function<void(const IndexDescriptor*)> onDropFn) = 0; - virtual void dropAllIndexes(OperationContext* opCtx, bool includingIdIndex) = 0; + virtual void dropAllIndexes(OperationContext* opCtx, + Collection* collection, + bool includingIdIndex) = 0; /** * Drops the index given its descriptor. @@ -386,7 +396,9 @@ public: * The caller must hold the collection X lock and ensure no index builds are in progress on the * collection. */ - virtual Status dropIndex(OperationContext* const opCtx, const IndexDescriptor* const desc) = 0; + virtual Status dropIndex(OperationContext* const opCtx, + Collection* collection, + const IndexDescriptor* const desc) = 0; /** * Drops an unfinished index given its descriptor. @@ -394,6 +406,7 @@ public: * The caller must hold the collection X lock. */ virtual Status dropUnfinishedIndex(OperationContext* const opCtx, + Collection* collection, const IndexDescriptor* const desc) = 0; /** @@ -401,12 +414,16 @@ public: * * The caller must hold the collection X lock. */ - virtual Status dropIndexEntry(OperationContext* opCtx, IndexCatalogEntry* entry) = 0; + virtual Status dropIndexEntry(OperationContext* opCtx, + Collection* collection, + IndexCatalogEntry* entry) = 0; /** * Deletes the index from the durable catalog on disk. */ - virtual void deleteIndexFromDisk(OperationContext* opCtx, const std::string& indexName) = 0; + virtual void deleteIndexFromDisk(OperationContext* opCtx, + Collection* collection, + const std::string& indexName) = 0; // ---- modify single index @@ -432,7 +449,7 @@ public: virtual Status indexRecords(OperationContext* const opCtx, const CollectionPtr& collection, const std::vector<BsonRecord>& bsonRecords, - int64_t* const keysInsertedOut) = 0; + int64_t* const keysInsertedOut) const = 0; /** * Both 'keysInsertedOut' and 'keysDeletedOut' are required and will be set to the number of @@ -446,17 +463,18 @@ public: const BSONObj& newDoc, const RecordId& recordId, int64_t* const keysInsertedOut, - int64_t* const keysDeletedOut) = 0; + int64_t* const keysDeletedOut) const = 0; /** * When 'keysDeletedOut' is not null, it will be set to the number of index keys removed by * this operation. */ virtual void unindexRecord(OperationContext* const opCtx, + const CollectionPtr& collection, const BSONObj& obj, const RecordId& loc, const bool noWarn, - int64_t* const keysDeletedOut) = 0; + int64_t* const keysDeletedOut) const = 0; /* * Attempt compaction on all ready indexes to regain disk space, if the storage engine's index @@ -489,7 +507,7 @@ public: InsertDeleteOptions* options) const = 0; virtual void indexBuildSuccess(OperationContext* opCtx, - const CollectionPtr& coll, + Collection* coll, IndexCatalogEntry* index) = 0; }; } // namespace mongo diff --git a/src/mongo/db/catalog/index_catalog_entry.h b/src/mongo/db/catalog/index_catalog_entry.h index 59658f6db32..5dd6acb6bd8 100644 --- a/src/mongo/db/catalog/index_catalog_entry.h +++ b/src/mongo/db/catalog/index_catalog_entry.h @@ -73,9 +73,7 @@ public: virtual const IndexDescriptor* descriptor() const = 0; - virtual IndexAccessMethod* accessMethod() = 0; - - virtual const IndexAccessMethod* accessMethod() const = 0; + virtual IndexAccessMethod* accessMethod() const = 0; virtual bool isHybridBuilding() const = 0; @@ -137,7 +135,7 @@ public: virtual void setMultikey(OperationContext* const opCtx, const CollectionPtr& coll, const KeyStringSet& multikeyMetadataKeys, - const MultikeyPaths& multikeyPaths) = 0; + const MultikeyPaths& multikeyPaths) const = 0; /** * Sets the index to be multikey with the provided paths. This performs minimal validation of @@ -150,14 +148,14 @@ public: virtual void forceSetMultikey(OperationContext* const opCtx, const CollectionPtr& coll, bool isMultikey, - const MultikeyPaths& multikeyPaths) = 0; + const MultikeyPaths& multikeyPaths) const = 0; /** * Returns whether this index is ready for queries. This is potentially unsafe in that it does * not consider whether the index is visible or ready in the current storage snapshot. For * that, use isReadyInMySnapshot() or isPresentInMySnapshot(). */ - virtual bool isReady(OperationContext* const opCtx) const = 0; + virtual bool isReady(OperationContext* opCtx, const CollectionPtr& collection) const = 0; /** * Safely check whether this index is visible in the durable catalog in the current storage diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp index 79c6f6f5466..c20bc831c88 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp @@ -61,24 +61,24 @@ namespace mongo { using std::string; IndexCatalogEntryImpl::IndexCatalogEntryImpl(OperationContext* const opCtx, - RecordId catalogId, + const CollectionPtr& collection, const std::string& ident, std::unique_ptr<IndexDescriptor> descriptor, bool isFrozen) : _ident(ident), _descriptor(std::move(descriptor)), - _catalogId(catalogId), + _catalogId(collection->getCatalogId()), _ordering(Ordering::make(_descriptor->keyPattern())), _isReady(false), _isFrozen(isFrozen), _isDropped(false) { _descriptor->_entry = this; - _isReady = isReadyInMySnapshot(opCtx); + _isReady = collection->isIndexReady(_descriptor->indexName()); { stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); - const bool isMultikey = _catalogIsMultikey(opCtx, &_indexMultikeyPathsForWrite); + const bool isMultikey = _catalogIsMultikey(opCtx, collection, &_indexMultikeyPathsForWrite); _isMultikeyForRead.store(isMultikey); _isMultikeyForWrite.store(isMultikey); _indexMultikeyPathsForRead = _indexMultikeyPathsForWrite; @@ -124,7 +124,8 @@ void IndexCatalogEntryImpl::init(std::unique_ptr<IndexAccessMethod> accessMethod _accessMethod = std::move(accessMethod); } -bool IndexCatalogEntryImpl::isReady(OperationContext* opCtx) const { +bool IndexCatalogEntryImpl::isReady(OperationContext* opCtx, + const CollectionPtr& collection) const { // For multi-document transactions, we can open a snapshot prior to checking the // minimumSnapshotVersion on a collection. This means we are unprotected from reading // out-of-sync index catalog entries. To fix this, we uassert if we detect that the @@ -171,7 +172,7 @@ void IndexCatalogEntryImpl::setIsReady(bool newIsReady) { void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, const CollectionPtr& collection, const KeyStringSet& multikeyMetadataKeys, - const MultikeyPaths& multikeyPaths) { + const MultikeyPaths& multikeyPaths) const { // An index can either track path-level multikey information in the catalog or as metadata keys // in the index itself, but not both. invariant(!(_indexTracksMultikeyPathsInCatalog && multikeyMetadataKeys.size() > 0)); @@ -257,7 +258,7 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx, void IndexCatalogEntryImpl::forceSetMultikey(OperationContext* const opCtx, const CollectionPtr& coll, bool isMultikey, - const MultikeyPaths& multikeyPaths) { + const MultikeyPaths& multikeyPaths) const { invariant(opCtx->lockState()->isCollectionLockedForMode(coll->ns(), MODE_X)); // Don't check _indexTracksMultikeyPathsInCatalog because the caller may be intentionally trying @@ -266,17 +267,17 @@ void IndexCatalogEntryImpl::forceSetMultikey(OperationContext* const opCtx, // caller wants to upgrade this index because it knows exactly which paths are multikey. We rely // on the following function to make sure this upgrade only takes place on index types that // currently support path-level multikey path tracking. - DurableCatalog::get(opCtx)->forceSetIndexIsMultikey( - opCtx, _catalogId, _descriptor.get(), isMultikey, multikeyPaths); + coll->forceSetIndexIsMultikey(opCtx, _descriptor.get(), isMultikey, multikeyPaths); // The prior call to set the multikey metadata in the catalog does some validation and clean up // based on the inputs, so reset the multikey variables based on what is actually in the durable // catalog entry. { stdx::lock_guard<Latch> lk(_indexMultikeyPathsMutex); - const bool isMultikey = _catalogIsMultikey(opCtx, &_indexMultikeyPathsForWrite); - _isMultikeyForRead.store(isMultikey); - _isMultikeyForWrite.store(isMultikey); + const bool isMultikeyInCatalog = + _catalogIsMultikey(opCtx, coll, &_indexMultikeyPathsForWrite); + _isMultikeyForRead.store(isMultikeyInCatalog); + _isMultikeyForWrite.store(isMultikeyInCatalog); _indexMultikeyPathsForRead = _indexMultikeyPathsForWrite; _indexTracksMultikeyPathsInCatalog = !_indexMultikeyPathsForWrite.empty(); } @@ -286,7 +287,9 @@ void IndexCatalogEntryImpl::forceSetMultikey(OperationContext* const opCtx, } Status IndexCatalogEntryImpl::_setMultikeyInMultiDocumentTransaction( - OperationContext* opCtx, const CollectionPtr& collection, const MultikeyPaths& multikeyPaths) { + OperationContext* opCtx, + const CollectionPtr& collection, + const MultikeyPaths& multikeyPaths) const { // If we are inside a multi-document transaction, we write the on-disk multikey update in a // separate transaction so that it will not generate prepare conflicts with other operations // that try to set the multikey flag. In general, it should always be safe to update the @@ -367,21 +370,21 @@ bool IndexCatalogEntryImpl::isPresentInMySnapshot(OperationContext* opCtx) const } bool IndexCatalogEntryImpl::_catalogIsMultikey(OperationContext* opCtx, + const CollectionPtr& collection, MultikeyPaths* multikeyPaths) const { - return DurableCatalog::get(opCtx)->isIndexMultikey( - opCtx, _catalogId, _descriptor->indexName(), multikeyPaths); + return collection->isIndexMultikey(opCtx, _descriptor->indexName(), multikeyPaths); } void IndexCatalogEntryImpl::_catalogSetMultikey(OperationContext* opCtx, const CollectionPtr& collection, - const MultikeyPaths& multikeyPaths) { + const MultikeyPaths& multikeyPaths) const { // It's possible that the index type (e.g. ascending/descending index) supports tracking // path-level multikey information, but this particular index doesn't. // CollectionCatalogEntry::setIndexIsMultikey() requires that we discard the path-level // multikey information in order to avoid unintentionally setting path-level multikey // information on an index created before 3.4. - auto indexMetadataHasChanged = DurableCatalog::get(opCtx)->setIndexIsMultikey( - opCtx, _catalogId, _descriptor->indexName(), multikeyPaths); + auto indexMetadataHasChanged = + collection->setIndexIsMultikey(opCtx, _descriptor->indexName(), multikeyPaths); // In the absence of using the storage engine to read from the catalog, we must set multikey // prior to the storage engine transaction committing. diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.h b/src/mongo/db/catalog/index_catalog_entry_impl.h index e2024294c7c..13bd86209fe 100644 --- a/src/mongo/db/catalog/index_catalog_entry_impl.h +++ b/src/mongo/db/catalog/index_catalog_entry_impl.h @@ -57,7 +57,7 @@ class IndexCatalogEntryImpl : public IndexCatalogEntry { public: IndexCatalogEntryImpl(OperationContext* opCtx, - RecordId catalogId, + const CollectionPtr& collection, const std::string& ident, std::unique_ptr<IndexDescriptor> descriptor, // ownership passes to me bool isFrozen); @@ -77,10 +77,7 @@ public: return _descriptor.get(); } - IndexAccessMethod* accessMethod() final { - return _accessMethod.get(); - } - const IndexAccessMethod* accessMethod() const final { + IndexAccessMethod* accessMethod() const final { return _accessMethod.get(); } @@ -158,14 +155,14 @@ public: void setMultikey(OperationContext* opCtx, const CollectionPtr& coll, const KeyStringSet& multikeyMetadataKeys, - const MultikeyPaths& multikeyPaths) final; + const MultikeyPaths& multikeyPaths) const final; void forceSetMultikey(OperationContext* const opCtx, const CollectionPtr& coll, bool isMultikey, - const MultikeyPaths& multikeyPaths) final; + const MultikeyPaths& multikeyPaths) const final; - bool isReady(OperationContext* opCtx) const final; + bool isReady(OperationContext* opCtx, const CollectionPtr& collection) const final; bool isFrozen() const final; @@ -194,21 +191,23 @@ private: */ Status _setMultikeyInMultiDocumentTransaction(OperationContext* opCtx, const CollectionPtr& collection, - const MultikeyPaths& multikeyPaths); + const MultikeyPaths& multikeyPaths) const; /** * Retrieves the multikey information associated with this index from '_collection', * * See CollectionCatalogEntry::isIndexMultikey() for more details. */ - bool _catalogIsMultikey(OperationContext* opCtx, MultikeyPaths* multikeyPaths) const; + bool _catalogIsMultikey(OperationContext* opCtx, + const CollectionPtr& collection, + MultikeyPaths* multikeyPaths) const; /** * Sets on-disk multikey flag for this index. */ void _catalogSetMultikey(OperationContext* opCtx, const CollectionPtr& collection, - const MultikeyPaths& multikeyPaths); + const MultikeyPaths& multikeyPaths) const; // ----- @@ -234,16 +233,20 @@ private: bool _isFrozen; AtomicWord<bool> _isDropped; // Whether the index drop is committed. + // Members for multikey are mutable so they can be changed in const functions. They are + // synchronized with the '_indexMultikeyPathsMutex' mutex or are atomic. We don't have the ABA + // problem as multikey may only go from disabled to enabled. When multikey it stays multikey. + // Set to true if this index can track path-level multikey information in the catalog. This // member is effectively const after IndexCatalogEntry::init() is called. - bool _indexTracksMultikeyPathsInCatalog = false; + mutable bool _indexTracksMultikeyPathsInCatalog = false; // Set to true if this index may contain multikey data. - AtomicWord<bool> _isMultikeyForRead; + mutable AtomicWord<bool> _isMultikeyForRead; // Set to true after a transaction commit successfully updates multikey on the catalog data. At // this point, future writers do not need to update the catalog. - AtomicWord<bool> _isMultikeyForWrite; + mutable AtomicWord<bool> _isMultikeyForWrite; // Controls concurrent access to '_indexMultikeyPathsForRead' and '_indexMultikeyPathsForWrite'. // We acquire this mutex rather than the RESOURCE_METADATA lock as a performance optimization @@ -258,8 +261,10 @@ private: // in the index key pattern. Each element in the vector is an ordered set of positions (starting // at 0) into the corresponding indexed field that represent what prefixes of the indexed field // causes the index to be multikey. - MultikeyPaths _indexMultikeyPathsForRead; // May include paths not committed to catalog. - MultikeyPaths _indexMultikeyPathsForWrite; // Paths in catalog updated by a transaction commit. + mutable MultikeyPaths + _indexMultikeyPathsForRead; // May include paths not committed to catalog. + mutable MultikeyPaths + _indexMultikeyPathsForWrite; // Paths in catalog updated by a transaction commit. // The earliest snapshot that is allowed to read this index. boost::optional<Timestamp> _minVisibleSnapshot; diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp index 815cafcf3c2..8ca5416cd37 100644 --- a/src/mongo/db/catalog/index_catalog_impl.cpp +++ b/src/mongo/db/catalog/index_catalog_impl.cpp @@ -97,20 +97,13 @@ const BSONObj IndexCatalogImpl::_idObj = BSON("_id" << 1); // ------------- -IndexCatalogImpl::IndexCatalogImpl(Collection* collection) : _collection(collection) {} - std::unique_ptr<IndexCatalog> IndexCatalogImpl::clone() const { return std::make_unique<IndexCatalogImpl>(*this); } -void IndexCatalogImpl::setCollection(Collection* collection) { - _collection = collection; -} - -Status IndexCatalogImpl::init(OperationContext* opCtx) { +Status IndexCatalogImpl::init(OperationContext* opCtx, Collection* collection) { vector<string> indexNames; - auto durableCatalog = DurableCatalog::get(opCtx); - durableCatalog->getAllIndexes(opCtx, _collection->getCatalogId(), &indexNames); + collection->getAllIndexes(&indexNames); const bool replSetMemberInStandaloneMode = getReplSetMemberInStandaloneMode(opCtx->getServiceContext()); @@ -122,8 +115,7 @@ Status IndexCatalogImpl::init(OperationContext* opCtx) { for (size_t i = 0; i < indexNames.size(); i++) { const string& indexName = indexNames[i]; - BSONObj spec = - durableCatalog->getIndexSpec(opCtx, _collection->getCatalogId(), indexName).getOwned(); + BSONObj spec = collection->getIndexSpec(indexName).getOwned(); BSONObj keyPattern = spec.getObjectField("key"); // TODO SERVER-51871: Delete this block once 5.0 becomes last-lts. @@ -138,18 +130,16 @@ Status IndexCatalogImpl::init(OperationContext* opCtx) { // TTL indexes are not compatible with capped collections. if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName) && - !_collection->isCapped()) { + !collection->isCapped()) { TTLCollectionCache::get(opCtx->getServiceContext()) - .registerTTLInfo(_collection->uuid(), indexName); + .registerTTLInfo(collection->uuid(), indexName); } - bool ready = durableCatalog->isIndexReady(opCtx, _collection->getCatalogId(), indexName); + bool ready = collection->isIndexReady(indexName); if (!ready) { - auto buildUUID = - durableCatalog->getIndexBuildUUID(opCtx, _collection->getCatalogId(), indexName); + auto buildUUID = collection->getIndexBuildUUID(indexName); invariant(buildUUID, - str::stream() - << "collection: " << _collection->ns() << "index:" << indexName); + str::stream() << "collection: " << collection->ns() << "index:" << indexName); // We intentionally do not drop or rebuild unfinished two-phase index builds before // initializing the IndexCatalog when starting a replica set member in standalone mode. // This is because the index build cannot complete until it receives a replicated commit @@ -158,18 +148,21 @@ Status IndexCatalogImpl::init(OperationContext* opCtx) { // Indicate that this index is "frozen". It is not ready but is not currently in // progress either. These indexes may be dropped. auto flags = CreateIndexEntryFlags::kInitFromDisk | CreateIndexEntryFlags::kFrozen; - IndexCatalogEntry* entry = createIndexEntry(opCtx, std::move(descriptor), flags); - fassert(31433, !entry->isReady(opCtx)); + IndexCatalogEntry* entry = + createIndexEntry(opCtx, collection, std::move(descriptor), flags); + fassert(31433, !entry->isReady(opCtx, collection)); } else { // Initializing with unfinished indexes may occur during rollback or startup. auto flags = CreateIndexEntryFlags::kInitFromDisk; - IndexCatalogEntry* entry = createIndexEntry(opCtx, std::move(descriptor), flags); - fassert(4505500, !entry->isReady(opCtx)); + IndexCatalogEntry* entry = + createIndexEntry(opCtx, collection, std::move(descriptor), flags); + fassert(4505500, !entry->isReady(opCtx, collection)); } } else { auto flags = CreateIndexEntryFlags::kInitFromDisk | CreateIndexEntryFlags::kIsReady; - IndexCatalogEntry* entry = createIndexEntry(opCtx, std::move(descriptor), flags); - fassert(17340, entry->isReady(opCtx)); + IndexCatalogEntry* entry = + createIndexEntry(opCtx, collection, std::move(descriptor), flags); + fassert(17340, entry->isReady(opCtx, collection)); // When initializing indexes from disk, we conservatively set the minimumVisibleSnapshot // to non _id indexes to the recovery timestamp. The _id index is left visible. It's @@ -180,7 +173,7 @@ Status IndexCatalogImpl::init(OperationContext* opCtx) { } } - CollectionQueryInfo::get(_collection).init(opCtx, _collection); + CollectionQueryInfo::get(collection).init(opCtx, collection); return Status::OK(); } @@ -227,19 +220,20 @@ string IndexCatalogImpl::_getAccessMethodName(const BSONObj& keyPattern) const { // --------------------------- StatusWith<BSONObj> IndexCatalogImpl::_validateAndFixIndexSpec(OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& original) const { - Status status = _isSpecOk(opCtx, original); + Status status = _isSpecOk(opCtx, collection, original); if (!status.isOK()) { return status; } - auto swFixed = _fixIndexSpec(opCtx, _collection, original); + auto swFixed = _fixIndexSpec(opCtx, collection, original); if (!swFixed.isOK()) { return swFixed; } // we double check with new index spec - status = _isSpecOk(opCtx, swFixed.getValue()); + status = _isSpecOk(opCtx, collection, swFixed.getValue()); if (!status.isOK()) { return status; } @@ -266,10 +260,11 @@ Status IndexCatalogImpl::_isNonIDIndexAndNotAllowedToBuild(OperationContext* opC } void IndexCatalogImpl::_logInternalState(OperationContext* opCtx, + const CollectionPtr& collection, long long numIndexesInCollectionCatalogEntry, const std::vector<std::string>& indexNamesToDrop, bool haveIdIndex) { - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); + invariant(opCtx->lockState()->isCollectionLockedForMode(collection->ns(), MODE_X)); LOGV2_ERROR(20365, "Internal Index Catalog state", @@ -302,40 +297,37 @@ void IndexCatalogImpl::_logInternalState(OperationContext* opCtx, std::vector<std::string> allIndexes; std::vector<std::string> readyIndexes; - auto durableCatalog = DurableCatalog::get(opCtx); - durableCatalog->getAllIndexes(opCtx, _collection->getCatalogId(), &allIndexes); - durableCatalog->getReadyIndexes(opCtx, _collection->getCatalogId(), &readyIndexes); + collection->getAllIndexes(&allIndexes); + collection->getReadyIndexes(&readyIndexes); for (const auto& index : allIndexes) { LOGV2_ERROR(20372, "allIndexes", "index"_attr = index, - "spec"_attr = redact( - durableCatalog->getIndexSpec(opCtx, _collection->getCatalogId(), index))); + "spec"_attr = redact(collection->getIndexSpec(index))); } for (const auto& index : readyIndexes) { LOGV2_ERROR(20374, "readyIndexes", "index"_attr = index, - "spec"_attr = redact( - durableCatalog->getIndexSpec(opCtx, _collection->getCatalogId(), index))); + "spec"_attr = redact(collection->getIndexSpec(index))); } for (const auto& indexNameToDrop : indexNamesToDrop) { LOGV2_ERROR(20376, "indexNamesToDrop", "index"_attr = indexNameToDrop, - "spec"_attr = redact(durableCatalog->getIndexSpec( - opCtx, _collection->getCatalogId(), indexNameToDrop))); + "spec"_attr = redact(collection->getIndexSpec(indexNameToDrop))); } } StatusWith<BSONObj> IndexCatalogImpl::prepareSpecForCreate( OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& original, const boost::optional<ResumeIndexInfo>& resumeInfo) const { - auto swValidatedAndFixed = _validateAndFixIndexSpec(opCtx, original); + auto swValidatedAndFixed = _validateAndFixIndexSpec(opCtx, collection, original); if (!swValidatedAndFixed.isOK()) { return swValidatedAndFixed.getStatus().withContext( str::stream() << "Error in specification " << original.toString()); @@ -360,7 +352,7 @@ StatusWith<BSONObj> IndexCatalogImpl::prepareSpecForCreate( } // First check against only the ready indexes for conflicts. - status = _doesSpecConflictWithExisting(opCtx, validatedSpec, false); + status = _doesSpecConflictWithExisting(opCtx, collection, validatedSpec, false); if (!status.isOK()) { return status; } @@ -376,7 +368,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, validatedSpec, true); + 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 @@ -390,7 +382,9 @@ StatusWith<BSONObj> IndexCatalogImpl::prepareSpecForCreate( } std::vector<BSONObj> IndexCatalogImpl::removeExistingIndexesNoChecks( - OperationContext* const opCtx, const std::vector<BSONObj>& indexSpecsToBuild) const { + OperationContext* const opCtx, + const CollectionPtr& collection, + const std::vector<BSONObj>& indexSpecsToBuild) const { std::vector<BSONObj> result; // Filter out ready and in-progress index builds, and any non-_id indexes if 'buildIndexes' is // set to false in the replica set's config. @@ -403,7 +397,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, spec, true /*includeUnfinishedIndexes*/)) { + _doesSpecConflictWithExisting( + opCtx, collection, spec, true /*includeUnfinishedIndexes*/)) { continue; } @@ -414,11 +409,12 @@ std::vector<BSONObj> IndexCatalogImpl::removeExistingIndexesNoChecks( std::vector<BSONObj> IndexCatalogImpl::removeExistingIndexes( OperationContext* const opCtx, + const CollectionPtr& collection, const std::vector<BSONObj>& indexSpecsToBuild, const bool removeIndexBuildsToo) const { std::vector<BSONObj> result; for (const auto& spec : indexSpecsToBuild) { - auto prepareResult = prepareSpecForCreate(opCtx, spec); + auto prepareResult = prepareSpecForCreate(opCtx, collection, spec); if (prepareResult == ErrorCodes::IndexAlreadyExists || (removeIndexBuildsToo && prepareResult == ErrorCodes::IndexBuildAlreadyInProgress)) { continue; @@ -430,20 +426,21 @@ std::vector<BSONObj> IndexCatalogImpl::removeExistingIndexes( } IndexCatalogEntry* IndexCatalogImpl::createIndexEntry(OperationContext* opCtx, + Collection* collection, std::unique_ptr<IndexDescriptor> descriptor, CreateIndexEntryFlags flags) { - Status status = _isSpecOk(opCtx, descriptor->infoObj()); + Status status = _isSpecOk(opCtx, collection, descriptor->infoObj()); if (!status.isOK()) { LOGV2_FATAL_NOTRACE(28782, "Found an invalid index", "descriptor"_attr = descriptor->infoObj(), - "namespace"_attr = _collection->ns(), + "namespace"_attr = collection->ns(), "error"_attr = redact(status)); } auto engine = opCtx->getServiceContext()->getStorageEngine(); std::string ident = engine->getCatalog()->getIndexIdent( - opCtx, _collection->getCatalogId(), descriptor->indexName()); + opCtx, collection->getCatalogId(), descriptor->indexName()); bool isReadyIndex = CreateIndexEntryFlags::kIsReady & flags; bool frozen = CreateIndexEntryFlags::kFrozen & flags; @@ -451,11 +448,10 @@ IndexCatalogEntry* IndexCatalogImpl::createIndexEntry(OperationContext* opCtx, auto* const descriptorPtr = descriptor.get(); auto entry = std::make_shared<IndexCatalogEntryImpl>( - opCtx, _collection->getCatalogId(), ident, std::move(descriptor), frozen); + opCtx, collection, ident, std::move(descriptor), frozen); IndexDescriptor* desc = entry->descriptor(); - auto collOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, _collection->getCatalogId()); + const auto& collOptions = collection->getCollectionOptions(); std::unique_ptr<SortedDataInterface> sdi = engine->getEngine()->getSortedDataInterface(opCtx, collOptions, ident, desc); @@ -473,10 +469,10 @@ IndexCatalogEntry* IndexCatalogImpl::createIndexEntry(OperationContext* opCtx, } bool initFromDisk = CreateIndexEntryFlags::kInitFromDisk & flags; - if (!initFromDisk && UncommittedCollections::getForTxn(opCtx, _collection->ns()) == nullptr) { + if (!initFromDisk && UncommittedCollections::getForTxn(opCtx, collection->ns()) == nullptr) { const std::string indexName = descriptorPtr->indexName(); opCtx->recoveryUnit()->onRollback( - [collectionDecorations = _collection->getSharedDecorations(), + [collectionDecorations = collection->getSharedDecorations(), indexName = std::move(indexName)] { CollectionIndexUsageTrackerDecoration::get(collectionDecorations) .unregisterIndex(indexName); @@ -487,15 +483,17 @@ IndexCatalogEntry* IndexCatalogImpl::createIndexEntry(OperationContext* opCtx, } StatusWith<BSONObj> IndexCatalogImpl::createIndexOnEmptyCollection(OperationContext* opCtx, + Collection* collection, BSONObj spec) { + invariant(collection->uuid() == collection->uuid()); UncommittedCollections::get(opCtx).invariantHasExclusiveAccessToCollection(opCtx, - _collection->ns()); - invariant(_collection->isEmpty(opCtx), - str::stream() << "Collection must be empty. Collection: " << _collection->ns() - << " UUID: " << _collection->uuid() - << " Count (from size storer): " << _collection->numRecords(opCtx)); + collection->ns()); + invariant(collection->isEmpty(opCtx), + str::stream() << "Collection must be empty. Collection: " << collection->ns() + << " UUID: " << collection->uuid() + << " Count (from size storer): " << collection->numRecords(opCtx)); - StatusWith<BSONObj> statusWithSpec = prepareSpecForCreate(opCtx, spec); + StatusWith<BSONObj> statusWithSpec = prepareSpecForCreate(opCtx, collection, spec); Status status = statusWithSpec.getStatus(); if (!status.isOK()) return status; @@ -504,13 +502,13 @@ StatusWith<BSONObj> IndexCatalogImpl::createIndexOnEmptyCollection(OperationCont // now going to touch disk boost::optional<UUID> buildUUID = boost::none; IndexBuildBlock indexBuildBlock( - _collection->ns(), spec, IndexBuildMethod::kForeground, buildUUID); - status = indexBuildBlock.init(opCtx, _collection); + collection->ns(), spec, IndexBuildMethod::kForeground, buildUUID); + status = indexBuildBlock.init(opCtx, collection); if (!status.isOK()) return status; // sanity checks, etc... - IndexCatalogEntry* entry = indexBuildBlock.getEntry(opCtx, _collection); + IndexCatalogEntry* entry = indexBuildBlock.getEntry(opCtx, collection); invariant(entry); IndexDescriptor* descriptor = entry->descriptor(); invariant(descriptor); @@ -518,11 +516,10 @@ StatusWith<BSONObj> IndexCatalogImpl::createIndexOnEmptyCollection(OperationCont status = entry->accessMethod()->initializeAsEmpty(opCtx); if (!status.isOK()) return status; - indexBuildBlock.success(opCtx, _collection); + indexBuildBlock.success(opCtx, collection); // sanity check - invariant(DurableCatalog::get(opCtx)->isIndexReady( - opCtx, _collection->getCatalogId(), descriptor->indexName())); + invariant(collection->isIndexReady(descriptor->indexName())); return spec; @@ -586,8 +583,10 @@ StatusWith<BSONObj> adjustIndexSpecObject(const BSONObj& obj) { } // namespace -Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec) const { - const NamespaceString& nss = _collection->ns(); +Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, + const CollectionPtr& collection, + const BSONObj& spec) const { + const NamespaceString& nss = collection->ns(); BSONElement vElt = spec["v"]; if (!vElt) { @@ -747,22 +746,22 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec) uassert(ErrorCodes::InvalidOptions, "Partial indexes are not supported on collections clustered by _id", - !_collection->isClustered() || !spec[IndexDescriptor::kPartialFilterExprFieldName]); + !collection->isClustered() || !spec[IndexDescriptor::kPartialFilterExprFieldName]); uassert(ErrorCodes::InvalidOptions, "Unique indexes are not supported on collections clustered by _id", - !_collection->isClustered() || !spec[IndexDescriptor::kUniqueFieldName].trueValue()); + !collection->isClustered() || !spec[IndexDescriptor::kUniqueFieldName].trueValue()); uassert(ErrorCodes::InvalidOptions, "TTL indexes are not supported on collections clustered by _id", - !_collection->isClustered() || !spec[IndexDescriptor::kExpireAfterSecondsFieldName]); + !collection->isClustered() || !spec[IndexDescriptor::kExpireAfterSecondsFieldName]); uassert(ErrorCodes::InvalidOptions, "Text indexes are not supported on collections clustered by _id", - !_collection->isClustered() || pluginName != IndexNames::TEXT); + !collection->isClustered() || pluginName != IndexNames::TEXT); if (IndexDescriptor::isIdIndexPattern(key)) { - if (_collection->isClustered()) { + if (collection->isClustered()) { return Status(ErrorCodes::CannotCreateIndex, "cannot create an _id index on a collection already clustered by _id"); } @@ -782,7 +781,7 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec) if (collationElement && !CollatorInterface::collatorsMatch(expCtx->getCollator(), - _collection->getDefaultCollator())) { + collection->getDefaultCollator())) { return Status(ErrorCodes::CannotCreateIndex, "_id index must have the collection default collation"); } @@ -816,6 +815,7 @@ Status IndexCatalogImpl::_isSpecOk(OperationContext* opCtx, const BSONObj& spec) } Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& spec, const bool includeUnfinishedIndexes) const { const char* name = spec.getStringField(IndexDescriptor::kIndexNameFieldName); @@ -831,7 +831,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, // Index already exists with same name. Check whether the options are the same as well. IndexDescriptor candidate(_getAccessMethodName(key), spec); auto indexComparison = - candidate.compareIndexOptions(opCtx, _collection->ns(), getEntry(desc)); + candidate.compareIndexOptions(opCtx, collection->ns(), getEntry(desc)); // Key pattern or another uniquely-identifying option differs. We can build this index, // but not with the specified (duplicate) name. User must specify another index name. @@ -893,7 +893,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, // informative error message. IndexDescriptor candidate(_getAccessMethodName(key), spec); auto indexComparison = - candidate.compareIndexOptions(opCtx, _collection->ns(), getEntry(desc)); + candidate.compareIndexOptions(opCtx, collection->ns(), getEntry(desc)); // The candidate's key and uniquely-identifying options are equivalent to an existing // index, but some other options are not identical. Return a message to that effect. @@ -914,11 +914,11 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, } if (numIndexesTotal(opCtx) >= kMaxNumIndexesAllowed) { - string s = str::stream() << "add index fails, too many indexes for " << _collection->ns() + string s = str::stream() << "add index fails, too many indexes for " << collection->ns() << " key:" << key; LOGV2(20354, "Exceeded maximum number of indexes", - "namespace"_attr = _collection->ns(), + "namespace"_attr = collection->ns(), "key"_attr = key, "maxNumIndexes"_attr = kMaxNumIndexesAllowed); return Status(ErrorCodes::CannotCreateIndex, s); @@ -940,7 +940,7 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx, return Status::OK(); } -BSONObj IndexCatalogImpl::getDefaultIdIndexSpec() const { +BSONObj IndexCatalogImpl::getDefaultIdIndexSpec(const CollectionPtr& collection) const { dassert(_idObj["_id"].type() == NumberInt); const auto indexVersion = IndexDescriptor::getDefaultIndexVersion(); @@ -949,18 +949,17 @@ BSONObj IndexCatalogImpl::getDefaultIdIndexSpec() const { b.append("v", static_cast<int>(indexVersion)); b.append("name", "_id_"); b.append("key", _idObj); - if (_collection->getDefaultCollator() && indexVersion >= IndexVersion::kV2) { + if (collection->getDefaultCollator() && indexVersion >= IndexVersion::kV2) { // Creating an index with the "collation" option requires a v=2 index. - b.append("collation", _collection->getDefaultCollator()->getSpec().toBSON()); + b.append("collation", collection->getDefaultCollator()->getSpec().toBSON()); } return b.obj(); } void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx, + Collection* collection, bool includingIdIndex, std::function<void(const IndexDescriptor*)> onDropFn) { - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); - uassert(ErrorCodes::BackgroundOperationInProgressForNamespace, str::stream() << "cannot perform operation: an index build is currently running", !haveAnyIndexesInProgress()); @@ -997,13 +996,12 @@ void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx, if (onDropFn) { onDropFn(desc); } - invariant(dropIndexEntry(opCtx, entry).isOK()); + invariant(dropIndexEntry(opCtx, collection, entry).isOK()); } // verify state is sane post cleaning - long long numIndexesInCollectionCatalogEntry = - DurableCatalog::get(opCtx)->getTotalIndexCount(opCtx, _collection->getCatalogId()); + long long numIndexesInCollectionCatalogEntry = collection->getTotalIndexCount(); if (haveIdIndex) { fassert(17324, numIndexesTotal(opCtx) == 1); @@ -1012,8 +1010,11 @@ void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx, fassert(17336, _readyIndexes.size() == 1); } else { if (numIndexesTotal(opCtx) || numIndexesInCollectionCatalogEntry || _readyIndexes.size()) { - _logInternalState( - opCtx, numIndexesInCollectionCatalogEntry, indexNamesToDrop, haveIdIndex); + _logInternalState(opCtx, + collection, + numIndexesInCollectionCatalogEntry, + indexNamesToDrop, + haveIdIndex); } fassert(17327, numIndexesTotal(opCtx) == 0); fassert(17328, numIndexesInCollectionCatalogEntry == 0); @@ -1021,36 +1022,38 @@ void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx, } } -void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx, bool includingIdIndex) { - dropAllIndexes(opCtx, includingIdIndex, {}); +void IndexCatalogImpl::dropAllIndexes(OperationContext* opCtx, + Collection* collection, + bool includingIdIndex) { + dropAllIndexes(opCtx, collection, includingIdIndex, {}); } -Status IndexCatalogImpl::dropIndex(OperationContext* opCtx, const IndexDescriptor* desc) { - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); - +Status IndexCatalogImpl::dropIndex(OperationContext* opCtx, + Collection* collection, + const IndexDescriptor* desc) { IndexCatalogEntry* entry = desc->getEntry(); if (!entry) return Status(ErrorCodes::InternalError, "cannot find index to delete"); - if (!entry->isReady(opCtx)) + if (!entry->isReady(opCtx, collection)) return Status(ErrorCodes::InternalError, "cannot delete not ready index"); - return dropIndexEntry(opCtx, entry); + return dropIndexEntry(opCtx, collection, entry); } -Status IndexCatalogImpl::dropUnfinishedIndex(OperationContext* opCtx, const IndexDescriptor* desc) { - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); - +Status IndexCatalogImpl::dropUnfinishedIndex(OperationContext* opCtx, + Collection* collection, + const IndexDescriptor* desc) { IndexCatalogEntry* entry = desc->getEntry(); if (!entry) return Status(ErrorCodes::InternalError, "cannot find index to delete"); - if (entry->isReady(opCtx)) + if (entry->isReady(opCtx, collection)) return Status(ErrorCodes::InternalError, "expected unfinished index, but it is ready"); - return dropIndexEntry(opCtx, entry); + return dropIndexEntry(opCtx, collection, entry); } namespace { @@ -1083,50 +1086,48 @@ private: }; } // namespace -Status IndexCatalogImpl::dropIndexEntry(OperationContext* opCtx, IndexCatalogEntry* entry) { +Status IndexCatalogImpl::dropIndexEntry(OperationContext* opCtx, + Collection* collection, + IndexCatalogEntry* entry) { invariant(entry); - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); // Pulling indexName out as it is needed post descriptor release. string indexName = entry->descriptor()->indexName(); - audit::logDropIndex(opCtx->getClient(), indexName, _collection->ns()); + audit::logDropIndex(opCtx->getClient(), indexName, collection->ns()); auto released = _readyIndexes.release(entry->descriptor()); if (released) { invariant(released.get() == entry); opCtx->recoveryUnit()->registerChange(std::make_unique<IndexRemoveChange>( - &_readyIndexes, std::move(released), _collection->getSharedDecorations())); + &_readyIndexes, std::move(released), collection->getSharedDecorations())); } else { released = _buildingIndexes.release(entry->descriptor()); invariant(released.get() == entry); opCtx->recoveryUnit()->registerChange(std::make_unique<IndexRemoveChange>( - &_buildingIndexes, std::move(released), _collection->getSharedDecorations())); + &_buildingIndexes, std::move(released), collection->getSharedDecorations())); } - CollectionQueryInfo::get(_collection).rebuildIndexData(opCtx, _collection); - CollectionIndexUsageTrackerDecoration::get(_collection->getSharedDecorations()) + CollectionQueryInfo::get(collection).rebuildIndexData(opCtx, collection); + CollectionIndexUsageTrackerDecoration::get(collection->getSharedDecorations()) .unregisterIndex(indexName); - _deleteIndexFromDisk(opCtx, indexName, entry->getSharedIdent()); + _deleteIndexFromDisk(opCtx, collection, indexName, entry->getSharedIdent()); return Status::OK(); } -void IndexCatalogImpl::deleteIndexFromDisk(OperationContext* opCtx, const string& indexName) { - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); - _deleteIndexFromDisk(opCtx, indexName, nullptr); +void IndexCatalogImpl::deleteIndexFromDisk(OperationContext* opCtx, + Collection* collection, + const string& indexName) { + _deleteIndexFromDisk(opCtx, collection, indexName, nullptr); } void IndexCatalogImpl::_deleteIndexFromDisk(OperationContext* opCtx, + Collection* collection, const string& indexName, std::shared_ptr<Ident> ident) { invariant(!findIndexByName(opCtx, indexName, true /* includeUnfinishedIndexes*/)); - catalog::removeIndex(opCtx, - indexName, - _collection->getCatalogId(), - _collection->uuid(), - _collection->ns(), - std::move(ident)); + catalog::removeIndex(opCtx, indexName, collection, std::move(ident)); } void IndexCatalogImpl::setMultikeyPaths(OperationContext* const opCtx, @@ -1276,13 +1277,12 @@ std::vector<std::shared_ptr<const IndexCatalogEntry>> IndexCatalogImpl::getAllRe } const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx, + Collection* collection, const IndexDescriptor* oldDesc) { - invariant(opCtx->lockState()->isCollectionLockedForMode(_collection->ns(), MODE_X)); invariant(_buildingIndexes.size() == 0); const std::string indexName = oldDesc->indexName(); - auto durableCatalog = DurableCatalog::get(opCtx); - invariant(durableCatalog->isIndexReady(opCtx, _collection->getCatalogId(), indexName)); + invariant(collection->isIndexReady(indexName)); // Delete the IndexCatalogEntry that owns this descriptor. After deletion, 'oldDesc' is // invalid and should not be dereferenced. Also, invalidate the index from the @@ -1290,26 +1290,26 @@ const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx, auto oldEntry = _readyIndexes.release(oldDesc); invariant(oldEntry); opCtx->recoveryUnit()->registerChange(std::make_unique<IndexRemoveChange>( - &_readyIndexes, std::move(oldEntry), _collection->getSharedDecorations())); - CollectionIndexUsageTrackerDecoration::get(_collection->getSharedDecorations()) + &_readyIndexes, std::move(oldEntry), collection->getSharedDecorations())); + CollectionIndexUsageTrackerDecoration::get(collection->getSharedDecorations()) .unregisterIndex(indexName); // Ask the CollectionCatalogEntry for the new index spec. - BSONObj spec = - durableCatalog->getIndexSpec(opCtx, _collection->getCatalogId(), indexName).getOwned(); + BSONObj spec = collection->getIndexSpec(indexName).getOwned(); BSONObj keyPattern = spec.getObjectField("key"); // Re-register this index in the index catalog with the new spec. Also, add the new index // to the CollectionIndexUsageTrackerDecoration (shared state among Collection instances). auto newDesc = std::make_unique<IndexDescriptor>(_getAccessMethodName(keyPattern), spec); - auto newEntry = createIndexEntry(opCtx, std::move(newDesc), CreateIndexEntryFlags::kIsReady); - invariant(newEntry->isReady(opCtx)); + auto newEntry = + createIndexEntry(opCtx, collection, std::move(newDesc), CreateIndexEntryFlags::kIsReady); + invariant(newEntry->isReady(opCtx, collection)); auto desc = newEntry->descriptor(); - CollectionIndexUsageTrackerDecoration::get(_collection->getSharedDecorations()) + CollectionIndexUsageTrackerDecoration::get(collection->getSharedDecorations()) .registerIndex(desc->indexName(), desc->keyPattern()); // Last rebuild index data for CollectionQueryInfo for this Collection. - CollectionQueryInfo::get(_collection).rebuildIndexData(opCtx, _collection); + CollectionQueryInfo::get(collection).rebuildIndexData(opCtx, collection); opCtx->recoveryUnit()->onCommit([newEntry](auto commitTime) { if (commitTime) { @@ -1325,14 +1325,14 @@ const IndexDescriptor* IndexCatalogImpl::refreshEntry(OperationContext* opCtx, Status IndexCatalogImpl::_indexKeys(OperationContext* opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const KeyStringSet& keys, const KeyStringSet& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const BSONObj& obj, RecordId loc, const InsertDeleteOptions& options, - int64_t* keysInsertedOut) { + int64_t* keysInsertedOut) const { Status status = Status::OK(); if (index->isHybridBuilding()) { // The side table interface accepts only records that meet the criteria for this partial @@ -1378,9 +1378,9 @@ Status IndexCatalogImpl::_indexKeys(OperationContext* opCtx, Status IndexCatalogImpl::_indexFilteredRecords(OperationContext* opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const std::vector<BsonRecord>& bsonRecords, - int64_t* keysInsertedOut) { + int64_t* keysInsertedOut) const { auto& executionCtx = StorageExecutionContext::get(opCtx); InsertDeleteOptions options; @@ -1429,9 +1429,9 @@ Status IndexCatalogImpl::_indexFilteredRecords(OperationContext* opCtx, Status IndexCatalogImpl::_indexRecords(OperationContext* opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const std::vector<BsonRecord>& bsonRecords, - int64_t* keysInsertedOut) { + int64_t* keysInsertedOut) const { if (MONGO_unlikely(skipIndexNewRecords.shouldFail())) { return Status::OK(); } @@ -1451,12 +1451,12 @@ Status IndexCatalogImpl::_indexRecords(OperationContext* opCtx, Status IndexCatalogImpl::_updateRecord(OperationContext* const opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const BSONObj& oldDoc, const BSONObj& newDoc, const RecordId& recordId, int64_t* const keysInsertedOut, - int64_t* const keysDeletedOut) { + int64_t* const keysDeletedOut) const { IndexAccessMethod* iam = index->accessMethod(); InsertDeleteOptions options; @@ -1470,10 +1470,10 @@ Status IndexCatalogImpl::_updateRecord(OperationContext* const opCtx, int64_t keysDeleted = 0; auto status = Status::OK(); - if (index->isHybridBuilding() || !index->isReady(opCtx)) { + if (index->isHybridBuilding() || !index->isReady(opCtx, coll)) { bool logIfError = false; _unindexKeys( - opCtx, index, updateTicket.removed, oldDoc, recordId, logIfError, &keysDeleted); + opCtx, coll, index, updateTicket.removed, oldDoc, recordId, logIfError, &keysDeleted); status = _indexKeys(opCtx, coll, index, @@ -1498,14 +1498,15 @@ Status IndexCatalogImpl::_updateRecord(OperationContext* const opCtx, } void IndexCatalogImpl::_unindexKeys(OperationContext* opCtx, - IndexCatalogEntry* index, + const CollectionPtr& collection, + const IndexCatalogEntry* index, const KeyStringSet& keys, const BSONObj& obj, RecordId loc, bool logIfError, - int64_t* const keysDeletedOut) { + int64_t* const keysDeletedOut) const { InsertDeleteOptions options; - prepareInsertDeleteOptions(opCtx, _collection->ns(), index->descriptor(), &options); + prepareInsertDeleteOptions(opCtx, collection->ns(), index->descriptor(), &options); options.logIfError = logIfError; if (index->isHybridBuilding()) { @@ -1537,7 +1538,7 @@ void IndexCatalogImpl::_unindexKeys(OperationContext* opCtx, // We need to disable blind-deletes for in-progress indexes, in order to force recordid-matching // for unindex operations, since initial sync can build an index over a collection with // duplicates. See SERVER-17487 for more details. - options.dupsAllowed = options.dupsAllowed || !index->isReady(opCtx); + options.dupsAllowed = options.dupsAllowed || !index->isReady(opCtx, collection); int64_t removed; Status status = index->accessMethod()->removeKeys(opCtx, keys, loc, options, &removed); @@ -1547,7 +1548,7 @@ void IndexCatalogImpl::_unindexKeys(OperationContext* opCtx, "Couldn't unindex record {obj} from collection {namespace}: {error}", "Couldn't unindex record", "record"_attr = redact(obj), - "namespace"_attr = _collection->ns(), + "namespace"_attr = collection->ns(), "error"_attr = redact(status)); } @@ -1557,11 +1558,12 @@ void IndexCatalogImpl::_unindexKeys(OperationContext* opCtx, } void IndexCatalogImpl::_unindexRecord(OperationContext* opCtx, - IndexCatalogEntry* entry, + const CollectionPtr& collection, + const IndexCatalogEntry* entry, const BSONObj& obj, const RecordId& loc, bool logIfError, - int64_t* keysDeletedOut) { + int64_t* keysDeletedOut) const { auto& executionCtx = StorageExecutionContext::get(opCtx); // There's no need to compute the prefixes of the indexed fields that cause the index to be @@ -1587,13 +1589,13 @@ void IndexCatalogImpl::_unindexRecord(OperationContext* opCtx, return; } } - _unindexKeys(opCtx, entry, *keys, obj, loc, logIfError, keysDeletedOut); + _unindexKeys(opCtx, collection, entry, *keys, obj, loc, logIfError, keysDeletedOut); } Status IndexCatalogImpl::indexRecords(OperationContext* opCtx, const CollectionPtr& coll, const std::vector<BsonRecord>& bsonRecords, - int64_t* keysInsertedOut) { + int64_t* keysInsertedOut) const { if (keysInsertedOut) { *keysInsertedOut = 0; } @@ -1619,7 +1621,7 @@ Status IndexCatalogImpl::updateRecord(OperationContext* const opCtx, const BSONObj& newDoc, const RecordId& recordId, int64_t* const keysInsertedOut, - int64_t* const keysDeletedOut) { + int64_t* const keysDeletedOut) const { *keysInsertedOut = 0; *keysDeletedOut = 0; @@ -1648,10 +1650,11 @@ Status IndexCatalogImpl::updateRecord(OperationContext* const opCtx, } void IndexCatalogImpl::unindexRecord(OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& obj, const RecordId& loc, bool noWarn, - int64_t* keysDeletedOut) { + int64_t* keysDeletedOut) const { if (keysDeletedOut) { *keysDeletedOut = 0; } @@ -1662,7 +1665,7 @@ void IndexCatalogImpl::unindexRecord(OperationContext* opCtx, IndexCatalogEntry* entry = it->get(); bool logIfError = !noWarn; - _unindexRecord(opCtx, entry, obj, loc, logIfError, keysDeletedOut); + _unindexRecord(opCtx, collection, entry, obj, loc, logIfError, keysDeletedOut); } for (IndexCatalogEntryContainer::const_iterator it = _buildingIndexes.begin(); @@ -1671,8 +1674,8 @@ void IndexCatalogImpl::unindexRecord(OperationContext* opCtx, IndexCatalogEntry* entry = it->get(); // If it's a background index, we DO NOT want to log anything. - bool logIfError = entry->isReady(opCtx) ? !noWarn : false; - _unindexRecord(opCtx, entry, obj, loc, logIfError, keysDeletedOut); + bool logIfError = entry->isReady(opCtx, collection) ? !noWarn : false; + _unindexRecord(opCtx, collection, entry, obj, loc, logIfError, keysDeletedOut); } } @@ -1740,7 +1743,7 @@ void IndexCatalogImpl::prepareInsertDeleteOptions(OperationContext* opCtx, } void IndexCatalogImpl::indexBuildSuccess(OperationContext* opCtx, - const CollectionPtr& coll, + Collection* coll, IndexCatalogEntry* index) { auto releasedEntry = _buildingIndexes.release(index->descriptor()); invariant(releasedEntry.get() == index); diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h index 83711ee9d92..1e8799889e3 100644 --- a/src/mongo/db/catalog/index_catalog_impl.h +++ b/src/mongo/db/catalog/index_catalog_impl.h @@ -58,25 +58,14 @@ struct InsertDeleteOptions; */ class IndexCatalogImpl : public IndexCatalog { public: - explicit IndexCatalogImpl(Collection* collection); - IndexCatalogImpl(const IndexCatalogImpl& other) = default; - /** * Creates a cloned IndexCatalogImpl. Will make shallow copies of IndexCatalogEntryContainers so * the IndexCatalogEntry will be shared across IndexCatalogImpl instances' - * - * Must call setCollection() after cloning to set the correct Collection backpointer */ std::unique_ptr<IndexCatalog> clone() const override; - /** - * Must be called after clone() to set the backpointer to the correct Collection instance. - * This is required due to limitations in cloned_ptr. - */ - void setCollection(Collection* collection); - // must be called before used - Status init(OperationContext* opCtx) override; + Status init(OperationContext* opCtx, Collection* collection) override; // ---- accessors ----- @@ -98,7 +87,7 @@ public: /** * Returns the spec for the id index to create by default for this collection. */ - BSONObj getDefaultIdIndexSpec() const override; + BSONObj getDefaultIdIndexSpec(const CollectionPtr& collection) const override; const IndexDescriptor* findIdIndex(OperationContext* opCtx) const override; @@ -170,6 +159,7 @@ public: * on the collection. */ const IndexDescriptor* refreshEntry(OperationContext* opCtx, + Collection* collection, const IndexDescriptor* oldDesc) override; const IndexCatalogEntry* getEntry(const IndexDescriptor* desc) const override; @@ -185,6 +175,7 @@ public: // ---- index set modifiers ------ IndexCatalogEntry* createIndexEntry(OperationContext* opCtx, + Collection* collection, std::unique_ptr<IndexDescriptor> descriptor, CreateIndexEntryFlags flags) override; @@ -194,19 +185,23 @@ public: * of the created index, as it is stored in this index catalog. */ StatusWith<BSONObj> createIndexOnEmptyCollection(OperationContext* opCtx, + Collection* collection, BSONObj spec) override; StatusWith<BSONObj> prepareSpecForCreate( OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& original, const boost::optional<ResumeIndexInfo>& resumeInfo = boost::none) const override; std::vector<BSONObj> removeExistingIndexes(OperationContext* const opCtx, + const CollectionPtr& collection, const std::vector<BSONObj>& indexSpecsToBuild, const bool removeIndexBuildsToo) const override; std::vector<BSONObj> removeExistingIndexesNoChecks( OperationContext* const opCtx, + const CollectionPtr& collection, const std::vector<BSONObj>& indexSpecsToBuild) const override; /** @@ -215,19 +210,29 @@ public: * it is filled with the names and index info of the dropped indexes. */ void dropAllIndexes(OperationContext* opCtx, + Collection* collection, bool includingIdIndex, std::function<void(const IndexDescriptor*)> onDropFn) override; - void dropAllIndexes(OperationContext* opCtx, bool includingIdIndex) override; + void dropAllIndexes(OperationContext* opCtx, + Collection* collection, + bool includingIdIndex) override; - Status dropIndex(OperationContext* opCtx, const IndexDescriptor* desc) override; + Status dropIndex(OperationContext* opCtx, + Collection* collection, + const IndexDescriptor* desc) override; Status dropUnfinishedIndex(OperationContext* const opCtx, + Collection* collection, const IndexDescriptor* const desc) override; - Status dropIndexEntry(OperationContext* opCtx, IndexCatalogEntry* entry) override; + Status dropIndexEntry(OperationContext* opCtx, + Collection* collection, + IndexCatalogEntry* entry) override; - void deleteIndexFromDisk(OperationContext* opCtx, const std::string& indexName) override; + void deleteIndexFromDisk(OperationContext* opCtx, + Collection* collection, + const std::string& indexName) override; struct IndexKillCriteria { std::string ns; @@ -254,7 +259,7 @@ public: Status indexRecords(OperationContext* opCtx, const CollectionPtr& coll, const std::vector<BsonRecord>& bsonRecords, - int64_t* keysInsertedOut) override; + int64_t* keysInsertedOut) const override; /** * See IndexCatalog::updateRecord @@ -265,16 +270,17 @@ public: const BSONObj& newDoc, const RecordId& recordId, int64_t* const keysInsertedOut, - int64_t* const keysDeletedOut) override; + int64_t* const keysDeletedOut) const override; /** * When 'keysDeletedOut' is not null, it will be set to the number of index keys removed by * this operation. */ void unindexRecord(OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& obj, const RecordId& loc, bool noWarn, - int64_t* keysDeletedOut) override; + int64_t* keysDeletedOut) const override; Status compactIndexes(OperationContext* opCtx) const override; @@ -298,7 +304,7 @@ public: InsertDeleteOptions* options) const override; void indexBuildSuccess(OperationContext* opCtx, - const CollectionPtr& collection, + Collection* coll, IndexCatalogEntry* index) override; private: @@ -314,56 +320,59 @@ private: Status _indexKeys(OperationContext* opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const KeyStringSet& keys, const KeyStringSet& multikeyMetadataKeys, const MultikeyPaths& multikeyPaths, const BSONObj& obj, RecordId loc, const InsertDeleteOptions& options, - int64_t* keysInsertedOut); + int64_t* keysInsertedOut) const; Status _indexFilteredRecords(OperationContext* opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const std::vector<BsonRecord>& bsonRecords, - int64_t* keysInsertedOut); + int64_t* keysInsertedOut) const; Status _indexRecords(OperationContext* opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const std::vector<BsonRecord>& bsonRecords, - int64_t* keysInsertedOut); + int64_t* keysInsertedOut) const; Status _updateRecord(OperationContext* const opCtx, const CollectionPtr& coll, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const BSONObj& oldDoc, const BSONObj& newDoc, const RecordId& recordId, int64_t* const keysInsertedOut, - int64_t* const keysDeletedOut); + int64_t* const keysDeletedOut) const; void _unindexKeys(OperationContext* opCtx, - IndexCatalogEntry* index, + const CollectionPtr& collection, + const IndexCatalogEntry* index, const KeyStringSet& keys, const BSONObj& obj, RecordId loc, bool logIfError, - int64_t* const keysDeletedOut); + int64_t* const keysDeletedOut) const; void _unindexRecord(OperationContext* opCtx, - IndexCatalogEntry* entry, + const CollectionPtr& collection, + const IndexCatalogEntry* entry, const BSONObj& obj, const RecordId& loc, bool logIfError, - int64_t* keysDeletedOut); + int64_t* keysDeletedOut) const; /** * Helper to remove the index from disk. * The index should be removed from the in-memory catalog beforehand. */ void _deleteIndexFromDisk(OperationContext* opCtx, + Collection* collection, const std::string& indexName, std::shared_ptr<Ident> ident); @@ -376,7 +385,9 @@ private: const CollectionPtr& collection, const BSONObj& spec) const; - Status _isSpecOk(OperationContext* opCtx, const BSONObj& spec) const; + Status _isSpecOk(OperationContext* opCtx, + const CollectionPtr& collection, + const BSONObj& spec) const; /** * Validates the 'original' index specification, alters any legacy fields and does plugin-level @@ -384,6 +395,7 @@ private: * error. */ StatusWith<BSONObj> _validateAndFixIndexSpec(OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& original) const; /** @@ -396,6 +408,7 @@ private: * errors. */ Status _doesSpecConflictWithExisting(OperationContext* opCtx, + const CollectionPtr& collection, const BSONObj& spec, const bool includeUnfinishedIndexes) const; @@ -407,12 +420,11 @@ private: Status _isNonIDIndexAndNotAllowedToBuild(OperationContext* opCtx, const BSONObj& spec) const; void _logInternalState(OperationContext* opCtx, + const CollectionPtr& collection, long long numIndexesInCollectionCatalogEntry, const std::vector<std::string>& indexNamesToDrop, bool haveIdIndex); - Collection* _collection; - IndexCatalogEntryContainer _readyIndexes; IndexCatalogEntryContainer _buildingIndexes; }; diff --git a/src/mongo/db/catalog/index_signature_test.cpp b/src/mongo/db/catalog/index_signature_test.cpp index e6bd673f1bb..fcf1d3d0b21 100644 --- a/src/mongo/db/catalog/index_signature_test.cpp +++ b/src/mongo/db/catalog/index_signature_test.cpp @@ -49,7 +49,8 @@ public: WriteUnitOfWork wuow(opCtx()); // Get the index catalog associated with the test collection. auto* indexCatalog = _coll->getWritableCollection()->getIndexCatalog(); - auto status = indexCatalog->createIndexOnEmptyCollection(opCtx(), spec); + auto status = indexCatalog->createIndexOnEmptyCollection( + opCtx(), _coll->getWritableCollection(), spec); if (!status.isOK()) { return status.getStatus(); } diff --git a/src/mongo/db/catalog/list_indexes.cpp b/src/mongo/db/catalog/list_indexes.cpp index 376d90ae9e4..c53f384bc0e 100644 --- a/src/mongo/db/catalog/list_indexes.cpp +++ b/src/mongo/db/catalog/list_indexes.cpp @@ -40,7 +40,6 @@ #include "mongo/db/db_raii.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/storage_engine.h" #include "mongo/util/uuid.h" @@ -69,37 +68,29 @@ std::list<BSONObj> listIndexesInLock(OperationContext* opCtx, boost::optional<bool> includeBuildUUIDs) { invariant(opCtx->lockState()->isCollectionLockedForMode(nss, MODE_IS)); - auto durableCatalog = DurableCatalog::get(opCtx); - CurOpFailpointHelpers::waitWhileFailPointEnabled( &hangBeforeListIndexes, opCtx, "hangBeforeListIndexes", []() {}, nss); return writeConflictRetry(opCtx, "listIndexes", nss.ns(), [&] { std::vector<std::string> indexNames; std::list<BSONObj> indexSpecs; - durableCatalog->getAllIndexes(opCtx, collection->getCatalogId(), &indexNames); + collection->getAllIndexes(&indexNames); for (size_t i = 0; i < indexNames.size(); i++) { - if (!includeBuildUUIDs.value_or(false) || - durableCatalog->isIndexReady(opCtx, collection->getCatalogId(), indexNames[i])) { - indexSpecs.push_back( - durableCatalog->getIndexSpec(opCtx, collection->getCatalogId(), indexNames[i])); + if (!includeBuildUUIDs.value_or(false) || collection->isIndexReady(indexNames[i])) { + indexSpecs.push_back(collection->getIndexSpec(indexNames[i])); continue; } // The durable catalog will not have a build UUID for the given index name if it was // not being built with two-phase. - const auto durableBuildUUID = DurableCatalog::get(opCtx)->getIndexBuildUUID( - opCtx, collection->getCatalogId(), indexNames[i]); + const auto durableBuildUUID = collection->getIndexBuildUUID(indexNames[i]); if (!durableBuildUUID) { - indexSpecs.push_back( - durableCatalog->getIndexSpec(opCtx, collection->getCatalogId(), indexNames[i])); + indexSpecs.push_back(collection->getIndexSpec(indexNames[i])); continue; } BSONObjBuilder builder; - builder.append( - "spec"_sd, - durableCatalog->getIndexSpec(opCtx, collection->getCatalogId(), indexNames[i])); + builder.append("spec"_sd, collection->getIndexSpec(indexNames[i])); durableBuildUUID->appendToBuilder(&builder, "buildUUID"_sd); indexSpecs.push_back(builder.obj()); } diff --git a/src/mongo/db/catalog/multi_index_block.cpp b/src/mongo/db/catalog/multi_index_block.cpp index fb51a69d4e7..af7e7868243 100644 --- a/src/mongo/db/catalog/multi_index_block.cpp +++ b/src/mongo/db/catalog/multi_index_block.cpp @@ -231,7 +231,8 @@ StatusWith<std::vector<BSONObj>> MultiIndexBlock::init( for (size_t i = 0; i < indexSpecs.size(); i++) { BSONObj info = indexSpecs[i]; StatusWith<BSONObj> statusWithInfo = - collection->getIndexCatalog()->prepareSpecForCreate(opCtx, info, resumeInfo); + collection->getIndexCatalog()->prepareSpecForCreate( + opCtx, collection.get(), info, resumeInfo); Status status = statusWithInfo.getStatus(); if (!status.isOK()) { // If we were given two identical indexes to build, we will run into an error trying diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index 66892ad359e..429f8100455 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -57,7 +57,6 @@ #include "mongo/db/s/operation_sharding_state.h" #include "mongo/db/server_options.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/views/view_catalog.h" #include "mongo/logv2/log.h" #include "mongo/util/fail_point.h" @@ -561,8 +560,7 @@ Status renameBetweenDBs(OperationContext* opCtx, Collection* tmpColl = nullptr; { - auto collectionOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, sourceColl->getCatalogId()); + auto collectionOptions = sourceColl->getCollectionOptions(); // Renaming across databases will result in a new UUID. collectionOptions.uuid = UUID::gen(); @@ -760,10 +758,7 @@ void doLocalRenameIfOptionsAndIndexesHaveNotChanged(OperationContext* opCtx, // collection was dropped and recreated, as long as the new target collection has the same // options and indexes as the original one did. This is mainly to support concurrent $out // to the same collection. - collectionOptions = DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .toBSON() - .removeField("uuid"); + collectionOptions = collection->getCollectionOptions().toBSON().removeField("uuid"); } uassert(ErrorCodes::CommandFailed, diff --git a/src/mongo/db/catalog/rename_collection_test.cpp b/src/mongo/db/catalog/rename_collection_test.cpp index 35eacf51294..39bb83b448e 100644 --- a/src/mongo/db/catalog/rename_collection_test.cpp +++ b/src/mongo/db/catalog/rename_collection_test.cpp @@ -57,7 +57,6 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_mock.h" #include "mongo/db/service_context_d_test_fixture.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" #include "mongo/util/assert_util.h" @@ -424,7 +423,7 @@ CollectionOptions _getCollectionOptions(OperationContext* opCtx, const Namespace AutoGetCollectionForRead collection(opCtx, nss); ASSERT_TRUE(collection) << "Unable to get collections options for " << nss << " because collection does not exist."; - return DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + return collection->getCollectionOptions(); } /** @@ -472,7 +471,10 @@ void _createIndexOnEmptyCollection(OperationContext* opCtx, WriteUnitOfWork wuow(opCtx); auto indexCatalog = collection.getWritableCollection()->getIndexCatalog(); - ASSERT_OK(indexCatalog->createIndexOnEmptyCollection(opCtx, indexInfoObj).getStatus()); + ASSERT_OK(indexCatalog + ->createIndexOnEmptyCollection( + opCtx, collection.getWritableCollection(), indexInfoObj) + .getStatus()); wuow.commit(); }); diff --git a/src/mongo/db/catalog/uncommitted_collections.cpp b/src/mongo/db/catalog/uncommitted_collections.cpp index ad87bce352b..31ace2c192e 100644 --- a/src/mongo/db/catalog/uncommitted_collections.cpp +++ b/src/mongo/db/catalog/uncommitted_collections.cpp @@ -33,7 +33,6 @@ #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/catalog/uncommitted_collections.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/util/assert_util.h" namespace mongo { diff --git a/src/mongo/db/catalog/uncommitted_multikey.cpp b/src/mongo/db/catalog/uncommitted_multikey.cpp new file mode 100644 index 00000000000..b7a48769912 --- /dev/null +++ b/src/mongo/db/catalog/uncommitted_multikey.cpp @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2021-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/catalog/uncommitted_multikey.h" +#include "mongo/db/operation_context.h" + +namespace mongo { +namespace { + +const auto getUncommittedMultikey = OperationContext::declareDecoration<UncommittedMultikey>(); +} // namespace + +UncommittedMultikey& UncommittedMultikey::get(OperationContext* opCtx) { + return getUncommittedMultikey(opCtx); +} + +} // namespace mongo diff --git a/src/mongo/db/catalog/uncommitted_multikey.h b/src/mongo/db/catalog/uncommitted_multikey.h new file mode 100644 index 00000000000..ff10f3f599e --- /dev/null +++ b/src/mongo/db/catalog/uncommitted_multikey.h @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2021-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/storage/bson_collection_catalog_entry.h" +#include <map> + +namespace mongo { +class Collection; +class UncommittedMultikey { +public: + /** + * Wrapper class for the resources used by UncommittedMultikey + * Store uncommitted multikey updates as a decoration on the OperationContext. We can use the + * raw Collection pointer as a key as there cannot be any concurrent MODE_X writer that clones + * the Collection into a new instance. + */ + using MultikeyMap = std::map<const Collection*, BSONCollectionCatalogEntry::MetaData>; + + std::shared_ptr<MultikeyMap> releaseResources() { + return std::move(_resourcesPtr); + } + + void receiveResources(std::shared_ptr<MultikeyMap> resources) { + _resourcesPtr = std::move(resources); + } + + static UncommittedMultikey& get(OperationContext* opCtx); + + std::shared_ptr<MultikeyMap>& resources() { + return _resourcesPtr; + } + +private: + std::shared_ptr<MultikeyMap> _resourcesPtr; +}; +} // namespace mongo diff --git a/src/mongo/db/catalog/validate_state.cpp b/src/mongo/db/catalog/validate_state.cpp index 19dc5dbfb3a..3a3a3f1b4cc 100644 --- a/src/mongo/db/catalog/validate_state.cpp +++ b/src/mongo/db/catalog/validate_state.cpp @@ -40,7 +40,6 @@ #include "mongo/db/db_raii.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/operation_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/views/view_catalog.h" #include "mongo/logv2/log.h" #include "mongo/util/fail_point.h" diff --git a/src/mongo/db/catalog/validate_state_test.cpp b/src/mongo/db/catalog/validate_state_test.cpp index ac023d807dd..bedbec3b99e 100644 --- a/src/mongo/db/catalog/validate_state_test.cpp +++ b/src/mongo/db/catalog/validate_state_test.cpp @@ -40,7 +40,6 @@ #include "mongo/db/op_observer_impl.h" #include "mongo/db/op_observer_registry.h" #include "mongo/db/repl/replication_coordinator.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/snapshot_manager.h" #include "mongo/db/storage/wiredtiger/wiredtiger_global_options.h" #include "mongo/util/fail_point.h" @@ -143,8 +142,8 @@ void dropIndex(OperationContext* opCtx, const NamespaceString& nss, const std::s auto indexDescriptor = collection->getIndexCatalog()->findIndexByName(opCtx, indexName); ASSERT(indexDescriptor); - ASSERT_OK( - collection.getWritableCollection()->getIndexCatalog()->dropIndex(opCtx, indexDescriptor)); + ASSERT_OK(collection.getWritableCollection()->getIndexCatalog()->dropIndex( + opCtx, collection.getWritableCollection(), indexDescriptor)); wuow.commit(); } diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp index 199dcf331ea..e0ff1859e51 100644 --- a/src/mongo/db/cloner.cpp +++ b/src/mongo/db/cloner.cpp @@ -61,7 +61,6 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/s/operation_sharding_state.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/logv2/log.h" #include "mongo/util/assert_util.h" #include "mongo/util/fail_point.h" @@ -294,7 +293,7 @@ void Cloner::_copyIndexes(OperationContext* opCtx, auto indexCatalog = collection->getIndexCatalog(); auto indexesToBuild = indexCatalog->removeExistingIndexesNoChecks( - opCtx, {std::begin(from_indexes), std::end(from_indexes)}); + opCtx, collection.get(), {std::begin(from_indexes), std::end(from_indexes)}); if (indexesToBuild.empty()) { return; } @@ -382,8 +381,7 @@ Status Cloner::_createCollectionsForDb( // exists on the target, we check if the existing collection's UUID matches // that of the one we're trying to create. If it does, we treat the create // as a no-op; if it doesn't match, we return an error. - auto existingOpts = DurableCatalog::get(opCtx)->getCollectionOptions( - opCtx, collection->getCatalogId()); + const auto& existingOpts = collection->getCollectionOptions(); const UUID clonedUUID = uassertStatusOK(UUID::parse(params.collectionInfo["info"]["uuid"])); diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index 74997b4ad5e..25563a57492 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -220,7 +220,7 @@ std::vector<BSONObj> resolveDefaultsAndRemoveExistingIndexes(OperationContext* o auto normalSpecs = IndexBuildsCoordinator::normalizeIndexSpecs(opCtx, collection, indexSpecs); return collection->getIndexCatalog()->removeExistingIndexes( - opCtx, normalSpecs, false /*removeIndexBuildsToo*/); + opCtx, collection, normalSpecs, false /*removeIndexBuildsToo*/); } /** diff --git a/src/mongo/db/commands/drop_indexes.cpp b/src/mongo/db/commands/drop_indexes.cpp index 30b2b1612e7..544709fec9d 100644 --- a/src/mongo/db/commands/drop_indexes.cpp +++ b/src/mongo/db/commands/drop_indexes.cpp @@ -51,7 +51,6 @@ #include "mongo/db/index_builds_coordinator.h" #include "mongo/db/op_observer.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/timeseries/timeseries_index_schema_conversion_functions.h" #include "mongo/db/timeseries/timeseries_lookup.h" #include "mongo/db/vector_clock.h" @@ -210,8 +209,7 @@ public: std::vector<std::string> indexNames; writeConflictRetry(opCtx, "listIndexes", toReIndexNss.ns(), [&] { indexNames.clear(); - DurableCatalog::get(opCtx)->getAllIndexes( - opCtx, collection->getCatalogId(), &indexNames); + collection->getAllIndexes(&indexNames); }); all.reserve(indexNames.size()); @@ -219,8 +217,7 @@ public: for (size_t i = 0; i < indexNames.size(); i++) { const std::string& name = indexNames[i]; BSONObj spec = writeConflictRetry(opCtx, "getIndexSpec", toReIndexNss.ns(), [&] { - return DurableCatalog::get(opCtx)->getIndexSpec( - opCtx, collection->getCatalogId(), name); + return collection->getIndexSpec(name); }); { @@ -261,7 +258,8 @@ public: "Uninitialized"); writeConflictRetry(opCtx, "dropAllIndexes", toReIndexNss.ns(), [&] { WriteUnitOfWork wunit(opCtx); - collection.getWritableCollection()->getIndexCatalog()->dropAllIndexes(opCtx, true); + collection.getWritableCollection()->getIndexCatalog()->dropAllIndexes( + opCtx, collection.getWritableCollection(), true); swIndexesToRebuild = indexer->init(opCtx, collection, all, MultiIndexBlock::kNoopOnInitFn); diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp index c8fa5630521..03c012f06fa 100644 --- a/src/mongo/db/commands/list_collections.cpp +++ b/src/mongo/db/commands/list_collections.cpp @@ -61,7 +61,6 @@ #include "mongo/db/query/find_common.h" #include "mongo/db/query/plan_executor_factory.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/storage_engine.h" #include "mongo/db/storage/storage_options.h" #include "mongo/db/views/view_catalog.h" @@ -192,19 +191,17 @@ BSONObj buildCollectionBson(OperationContext* opCtx, return b.obj(); } - CollectionOptions options = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + const auto& options = collection->getCollectionOptions(); // While the UUID is stored as a collection option, from the user's perspective it is an - // unsettable read-only property, so put it in the 'info' section. - auto uuid = options.uuid; - options.uuid.reset(); - b.append("options", options.toBSON()); + // unsettable read-only property, so put it in the 'info' section. Pass 'false' to toBSON so it + // doesn't include 'uuid' here. + b.append("options", options.toBSON(false)); BSONObjBuilder infoBuilder; infoBuilder.append("readOnly", storageGlobalParams.readOnly); - if (uuid) - infoBuilder.appendElements(uuid->toBSON()); + if (options.uuid) + infoBuilder.appendElements(options.uuid->toBSON()); b.append("info", infoBuilder.obj()); auto idIndex = collection->getIndexCatalog()->findIdIndex(opCtx); diff --git a/src/mongo/db/commands/mr_test.cpp b/src/mongo/db/commands/mr_test.cpp index eaada81ab68..1dd86a06946 100644 --- a/src/mongo/db/commands/mr_test.cpp +++ b/src/mongo/db/commands/mr_test.cpp @@ -537,7 +537,7 @@ TEST_F(MapReduceCommandTest, ReplacingExistingOutputCollectionPreservesIndexes) WriteUnitOfWork wuow(_opCtx.get()); ASSERT_OK( coll.getWritableCollection()->getIndexCatalog()->createIndexOnEmptyCollection( - _opCtx.get(), indexSpec)); + _opCtx.get(), coll.getWritableCollection(), indexSpec)); wuow.commit(); }); } diff --git a/src/mongo/db/commands/resize_oplog.cpp b/src/mongo/db/commands/resize_oplog.cpp index e8a54b818e8..5476c4ea249 100644 --- a/src/mongo/db/commands/resize_oplog.cpp +++ b/src/mongo/db/commands/resize_oplog.cpp @@ -98,8 +98,6 @@ public: if (auto sizeMB = params.getSize()) { const long long sizeBytes = *sizeMB * 1024 * 1024; uassertStatusOK(coll.getWritableCollection()->updateCappedSize(opCtx, sizeBytes)); - DurableCatalog::get(opCtx)->updateCappedSize( - opCtx, coll->getCatalogId(), sizeBytes); } if (auto minRetentionHoursOpt = params.getMinRetentionHours()) { @@ -109,9 +107,7 @@ public: LOGV2(20497, "replSetResizeOplog success", - "size"_attr = DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, coll->getCatalogId()) - .cappedSize, + "size"_attr = coll->getCollectionOptions().cappedSize, "minRetentionHours"_attr = storageGlobalParams.oplogMinRetentionHours.load()); return true; }); diff --git a/src/mongo/db/commands/validate.cpp b/src/mongo/db/commands/validate.cpp index bdde2c3156a..6e2889eb741 100644 --- a/src/mongo/db/commands/validate.cpp +++ b/src/mongo/db/commands/validate.cpp @@ -36,7 +36,6 @@ #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/query/internal_plans.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/record_store.h" #include "mongo/logv2/log.h" #include "mongo/util/fail_point.h" diff --git a/src/mongo/db/commands/write_commands.cpp b/src/mongo/db/commands/write_commands.cpp index 04efea03dc5..2e3caeb78dd 100644 --- a/src/mongo/db/commands/write_commands.cpp +++ b/src/mongo/db/commands/write_commands.cpp @@ -63,7 +63,6 @@ #include "mongo/db/retryable_writes_stats.h" #include "mongo/db/stats/counters.h" #include "mongo/db/storage/duplicate_key_error_info.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/timeseries/bucket_catalog.h" #include "mongo/db/transaction_participant.h" #include "mongo/db/views/view_catalog.h" diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp index 57a609f1afd..b906b593150 100644 --- a/src/mongo/db/index/index_access_method.cpp +++ b/src/mongo/db/index/index_access_method.cpp @@ -50,7 +50,6 @@ #include "mongo/db/operation_context.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/timestamp_block.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/execution_context.h" #include "mongo/db/storage/storage_options.h" #include "mongo/logv2/log.h" @@ -108,7 +107,7 @@ struct BtreeExternalSortComparison { } }; -AbstractIndexAccessMethod::AbstractIndexAccessMethod(IndexCatalogEntry* btreeState, +AbstractIndexAccessMethod::AbstractIndexAccessMethod(const IndexCatalogEntry* btreeState, std::unique_ptr<SortedDataInterface> btree) : _indexCatalogEntry(btreeState), _descriptor(btreeState->descriptor()), @@ -377,7 +376,7 @@ pair<KeyStringSet, KeyStringSet> AbstractIndexAccessMethod::setDifference( } void AbstractIndexAccessMethod::prepareUpdate(OperationContext* opCtx, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const BSONObj& from, const BSONObj& to, const RecordId& record, @@ -476,11 +475,11 @@ Status AbstractIndexAccessMethod::compact(OperationContext* opCtx) { class AbstractIndexAccessMethod::BulkBuilderImpl : public IndexAccessMethod::BulkBuilder { public: - BulkBuilderImpl(IndexCatalogEntry* indexCatalogEntry, + BulkBuilderImpl(const IndexCatalogEntry* indexCatalogEntry, size_t maxMemoryUsageBytes, StringData dbName); - BulkBuilderImpl(IndexCatalogEntry* index, + BulkBuilderImpl(const IndexCatalogEntry* index, size_t maxMemoryUsageBytes, const IndexStateInfo& stateInfo, StringData dbName); @@ -515,7 +514,7 @@ private: Sorter::Settings _makeSorterSettings() const; - IndexCatalogEntry* _indexCatalogEntry; + const IndexCatalogEntry* _indexCatalogEntry; std::unique_ptr<Sorter> _sorter; int64_t _keysInserted = 0; @@ -542,12 +541,12 @@ std::unique_ptr<IndexAccessMethod::BulkBuilder> AbstractIndexAccessMethod::initi : std::make_unique<BulkBuilderImpl>(_indexCatalogEntry, maxMemoryUsageBytes, dbName); } -AbstractIndexAccessMethod::BulkBuilderImpl::BulkBuilderImpl(IndexCatalogEntry* index, +AbstractIndexAccessMethod::BulkBuilderImpl::BulkBuilderImpl(const IndexCatalogEntry* index, size_t maxMemoryUsageBytes, StringData dbName) : _indexCatalogEntry(index), _sorter(_makeSorter(maxMemoryUsageBytes, dbName)) {} -AbstractIndexAccessMethod::BulkBuilderImpl::BulkBuilderImpl(IndexCatalogEntry* index, +AbstractIndexAccessMethod::BulkBuilderImpl::BulkBuilderImpl(const IndexCatalogEntry* index, size_t maxMemoryUsageBytes, const IndexStateInfo& stateInfo, StringData dbName) diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h index 5e3c9376ea7..417af5798b0 100644 --- a/src/mongo/db/index/index_access_method.h +++ b/src/mongo/db/index/index_access_method.h @@ -138,7 +138,7 @@ public: * Provides a ticket for actually performing the update. */ virtual void prepareUpdate(OperationContext* opCtx, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const BSONObj& from, const BSONObj& to, const RecordId& loc, @@ -454,7 +454,7 @@ public: static std::pair<KeyStringSet, KeyStringSet> setDifference(const KeyStringSet& left, const KeyStringSet& right); - AbstractIndexAccessMethod(IndexCatalogEntry* btreeState, + AbstractIndexAccessMethod(const IndexCatalogEntry* btreeState, std::unique_ptr<SortedDataInterface> btree); Status insert(OperationContext* opCtx, @@ -490,7 +490,7 @@ public: int64_t* numDeleted) final; void prepareUpdate(OperationContext* opCtx, - IndexCatalogEntry* index, + const IndexCatalogEntry* index, const BSONObj& from, const BSONObj& to, const RecordId& loc, @@ -579,7 +579,7 @@ protected: MultikeyPaths* multikeyPaths, boost::optional<RecordId> id) const = 0; - IndexCatalogEntry* const _indexCatalogEntry; // owned by IndexCatalog + const IndexCatalogEntry* const _indexCatalogEntry; // owned by IndexCatalog const IndexDescriptor* const _descriptor; private: diff --git a/src/mongo/db/index_builds_coordinator.cpp b/src/mongo/db/index_builds_coordinator.cpp index ba9d2d46a56..c3cd97d8853 100644 --- a/src/mongo/db/index_builds_coordinator.cpp +++ b/src/mongo/db/index_builds_coordinator.cpp @@ -546,7 +546,8 @@ Status IndexBuildsCoordinator::_startIndexBuildForRecovery(OperationContext* opC auto descriptor = indexCatalog->findIndexByName(opCtx, indexNames[i], includeUnfinished); if (descriptor) { - Status s = indexCatalog->dropIndex(opCtx, descriptor); + Status s = + indexCatalog->dropIndex(opCtx, collection.getWritableCollection(), descriptor); if (!s.isOK()) { return s; } @@ -556,8 +557,7 @@ Status IndexBuildsCoordinator::_startIndexBuildForRecovery(OperationContext* opC // If the index is not present in the catalog, then we are trying to drop an already // aborted index. This may happen when rollback-via-refetch restarts an index build // after an abort has been rolled back. - if (!DurableCatalog::get(opCtx)->isIndexPresent( - opCtx, collection->getCatalogId(), indexNames[i])) { + if (!collection->isIndexPresent(indexNames[i])) { LOGV2(20652, "An index was not found in the catalog while trying to drop the index during " "recovery", @@ -566,8 +566,7 @@ Status IndexBuildsCoordinator::_startIndexBuildForRecovery(OperationContext* opC continue; } - const auto durableBuildUUID = DurableCatalog::get(opCtx)->getIndexBuildUUID( - opCtx, collection->getCatalogId(), indexNames[i]); + const auto durableBuildUUID = collection->getIndexBuildUUID(indexNames[i]); // A build UUID is present if and only if we are rebuilding a two-phase build. invariant((protocol == IndexBuildProtocol::kTwoPhase) == @@ -584,7 +583,8 @@ Status IndexBuildsCoordinator::_startIndexBuildForRecovery(OperationContext* opC includeUnfinished = true; descriptor = indexCatalog->findIndexByName(opCtx, indexNames[i], includeUnfinished); if (descriptor) { - Status s = indexCatalog->dropUnfinishedIndex(opCtx, descriptor); + Status s = indexCatalog->dropUnfinishedIndex( + opCtx, collection.getWritableCollection(), descriptor); if (!s.isOK()) { return s; } @@ -592,12 +592,8 @@ Status IndexBuildsCoordinator::_startIndexBuildForRecovery(OperationContext* opC // There are no concurrent users of the index during startup recovery, so it is OK // to pass in a nullptr for the index 'ident', promising that the index is not in // use. - catalog::removeIndex(opCtx, - indexNames[i], - collection->getCatalogId(), - collection->uuid(), - collection->ns(), - nullptr /* ident */); + catalog::removeIndex( + opCtx, indexNames[i], collection.getWritableCollection(), nullptr /* ident */); } } @@ -664,10 +660,9 @@ Status IndexBuildsCoordinator::_setUpResumeIndexBuild(OperationContext* opCtx, // Check that the information in the durable catalog matches the resume info. uassert(4841702, "Index not found in durable catalog while attempting to resume index build", - durableCatalog->isIndexPresent(opCtx, collection->getCatalogId(), indexName)); + collection->isIndexPresent(indexName)); - const auto durableBuildUUID = - durableCatalog->getIndexBuildUUID(opCtx, collection->getCatalogId(), indexName); + const auto durableBuildUUID = collection->getIndexBuildUUID(indexName); uassert(ErrorCodes::IndexNotFound, str::stream() << "Cannot resume index build with a buildUUID: " << buildUUID << " that did not match the buildUUID in the durable catalog: " @@ -682,8 +677,7 @@ Status IndexBuildsCoordinator::_setUpResumeIndexBuild(OperationContext* opCtx, << indexName, indexIdent.size() > 0); - uassertStatusOK(durableCatalog->checkMetaDataForIndex( - opCtx, collection->getCatalogId(), indexName, spec)); + uassertStatusOK(collection->checkMetaDataForIndex(indexName, spec)); } if (!collection->isInitialized()) { @@ -859,7 +853,8 @@ void IndexBuildsCoordinator::applyStartIndexBuild(OperationContext* opCtx, !name.empty()); if (auto desc = indexCatalog->findIndexByName(opCtx, name, includeUnfinished)) { - uassertStatusOK(indexCatalog->dropIndex(opCtx, desc)); + uassertStatusOK( + indexCatalog->dropIndex(opCtx, coll.getWritableCollection(), desc)); } } @@ -1629,7 +1624,8 @@ void IndexBuildsCoordinator::createIndexesOnEmptyCollection(OperationContext* op // Each index will be added to the mdb catalog using the preceding createIndexes // timestamp. opObserver->onCreateIndex(opCtx, nss, collectionUUID, spec, fromMigrate); - uassertStatusOK(indexCatalog->createIndexOnEmptyCollection(opCtx, spec)); + uassertStatusOK(indexCatalog->createIndexOnEmptyCollection( + opCtx, collection.getWritableCollection(), spec)); } } @@ -2756,8 +2752,8 @@ std::vector<BSONObj> IndexBuildsCoordinator::prepareSpecListForCreate( // Remove any index specifications which already exist in the catalog. auto indexCatalog = collection->getIndexCatalog(); - auto resultSpecs = - indexCatalog->removeExistingIndexes(opCtx, normalSpecs, true /*removeIndexBuildsToo*/); + auto resultSpecs = indexCatalog->removeExistingIndexes( + opCtx, collection, normalSpecs, true /*removeIndexBuildsToo*/); // Verify that each spec is compatible with the collection's sharding state. for (const BSONObj& spec : resultSpecs) { diff --git a/src/mongo/db/index_builds_coordinator.h b/src/mongo/db/index_builds_coordinator.h index cf73da60d78..a899477ca17 100644 --- a/src/mongo/db/index_builds_coordinator.h +++ b/src/mongo/db/index_builds_coordinator.h @@ -48,7 +48,6 @@ #include "mongo/db/repl/oplog_entry.h" #include "mongo/db/repl_index_build_state.h" #include "mongo/db/resumable_index_builds_gen.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/executor/task_executor.h" #include "mongo/executor/thread_pool_task_executor.h" #include "mongo/platform/mutex.h" diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp index 22fa6f66d03..d1267aa5768 100644 --- a/src/mongo/db/op_observer_impl.cpp +++ b/src/mongo/db/op_observer_impl.cpp @@ -63,7 +63,6 @@ #include "mongo/db/s/resharding_util.h" #include "mongo/db/server_options.h" #include "mongo/db/session_catalog_mongod.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/timeseries/bucket_catalog.h" #include "mongo/db/transaction_participant.h" #include "mongo/db/transaction_participant_gen.h" @@ -896,7 +895,6 @@ void OpObserverImpl::onCollMod(OperationContext* opCtx, CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, nss); invariant(coll->uuid() == uuid); - invariant(DurableCatalog::get(opCtx)->isEqualToMetadataUUID(opCtx, coll->getCatalogId(), uuid)); } void OpObserverImpl::onDropDatabase(OperationContext* opCtx, const std::string& dbName) { diff --git a/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp b/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp index 719d242dd1a..95f4e91e925 100644 --- a/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp +++ b/src/mongo/db/pipeline/process_interface/common_mongod_process_interface.cpp @@ -65,7 +65,6 @@ #include "mongo/db/stats/fill_locker_info.h" #include "mongo/db/stats/storage_stats.h" #include "mongo/db/storage/backup_cursor_hooks.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/transaction_history_iterator.h" #include "mongo/db/transaction_participant.h" #include "mongo/logv2/log.h" @@ -285,9 +284,7 @@ BSONObj CommonMongodProcessInterface::getCollectionOptions(OperationContext* opC return collectionOptions; } - collectionOptions = DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .toBSON(); + collectionOptions = collection->getCollectionOptions().toBSON(); return collectionOptions; } diff --git a/src/mongo/db/pipeline/process_interface/non_shardsvr_process_interface.cpp b/src/mongo/db/pipeline/process_interface/non_shardsvr_process_interface.cpp index 26ae6cda451..3dc53e3e900 100644 --- a/src/mongo/db/pipeline/process_interface/non_shardsvr_process_interface.cpp +++ b/src/mongo/db/pipeline/process_interface/non_shardsvr_process_interface.cpp @@ -132,7 +132,7 @@ void NonShardServerProcessInterface::createIndexesOnEmptyCollection( // primary. auto removeIndexBuildsToo = false; auto filteredIndexes = collection->getIndexCatalog()->removeExistingIndexes( - opCtx, indexSpecs, removeIndexBuildsToo); + opCtx, collection.get(), indexSpecs, removeIndexBuildsToo); if (filteredIndexes.empty()) { return; } diff --git a/src/mongo/db/rebuild_indexes.cpp b/src/mongo/db/rebuild_indexes.cpp index 52962679162..c2986396d6a 100644 --- a/src/mongo/db/rebuild_indexes.cpp +++ b/src/mongo/db/rebuild_indexes.cpp @@ -40,20 +40,17 @@ #include "mongo/db/catalog/index_key_validate.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index_builds_coordinator.h" -#include "mongo/db/storage/durable_catalog.h" namespace mongo { -StatusWith<IndexNameObjs> getIndexNameObjs(OperationContext* opCtx, - RecordId catalogId, +StatusWith<IndexNameObjs> getIndexNameObjs(const CollectionPtr& collection, std::function<bool(const std::string&)> filter) { IndexNameObjs ret; std::vector<std::string>& indexNames = ret.first; std::vector<BSONObj>& indexSpecs = ret.second; - auto durableCatalog = DurableCatalog::get(opCtx); { // Fetch all indexes - durableCatalog->getAllIndexes(opCtx, catalogId, &indexNames); + collection->getAllIndexes(&indexNames); auto newEnd = std::remove_if(indexNames.begin(), indexNames.end(), @@ -64,7 +61,7 @@ StatusWith<IndexNameObjs> getIndexNameObjs(OperationContext* opCtx, for (const auto& name : indexNames) { - BSONObj spec = durableCatalog->getIndexSpec(opCtx, catalogId, name); + BSONObj spec = collection->getIndexSpec(name); using IndexVersion = IndexDescriptor::IndexVersion; IndexVersion indexVersion = IndexVersion::kV1; if (auto indexVersionElem = spec[IndexDescriptor::kIndexVersionFieldName]) { diff --git a/src/mongo/db/rebuild_indexes.h b/src/mongo/db/rebuild_indexes.h index ea6c0cc2a94..239781a4817 100644 --- a/src/mongo/db/rebuild_indexes.h +++ b/src/mongo/db/rebuild_indexes.h @@ -49,8 +49,7 @@ typedef std::pair<std::vector<std::string>, std::vector<BSONObj>> IndexNameObjs; * @param filter is a predicate that is passed in an index name, returning true if the index * should be included in the result. */ -StatusWith<IndexNameObjs> getIndexNameObjs(OperationContext* opCtx, - RecordId catalogId, +StatusWith<IndexNameObjs> getIndexNameObjs(const CollectionPtr& collection, std::function<bool(const std::string&)> filter = [](const std::string& indexName) { return true; }); diff --git a/src/mongo/db/repair.cpp b/src/mongo/db/repair.cpp index c58c300a234..700346f8eac 100644 --- a/src/mongo/db/repair.cpp +++ b/src/mongo/db/repair.cpp @@ -56,7 +56,6 @@ #include "mongo/db/query/query_knobs_gen.h" #include "mongo/db/rebuild_indexes.h" #include "mongo/db/repl_set_member_in_standalone_mode.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/storage_engine.h" #include "mongo/db/storage/storage_repair_observer.h" #include "mongo/db/storage/storage_util.h" @@ -73,7 +72,7 @@ Status rebuildIndexesForNamespace(OperationContext* opCtx, StorageEngine* engine) { opCtx->checkForInterrupt(); auto collection = CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, nss); - auto swIndexNameObjs = getIndexNameObjs(opCtx, collection->getCatalogId()); + auto swIndexNameObjs = getIndexNameObjs(collection); if (!swIndexNameObjs.isOK()) return swIndexNameObjs.getStatus(); @@ -87,12 +86,11 @@ Status rebuildIndexesForNamespace(OperationContext* opCtx, } namespace { -Status dropUnfinishedIndexes(OperationContext* opCtx, const CollectionPtr& collection) { +Status dropUnfinishedIndexes(OperationContext* opCtx, Collection* collection) { std::vector<std::string> indexNames; - auto durableCatalog = DurableCatalog::get(opCtx); - durableCatalog->getAllIndexes(opCtx, collection->getCatalogId(), &indexNames); + collection->getAllIndexes(&indexNames); for (const auto& indexName : indexNames) { - if (!durableCatalog->isIndexReady(opCtx, collection->getCatalogId(), indexName)) { + if (!collection->isIndexReady(indexName)) { LOGV2(3871400, "Dropping unfinished index '{name}' after collection was modified by " "repair", @@ -102,12 +100,7 @@ Status dropUnfinishedIndexes(OperationContext* opCtx, const CollectionPtr& colle WriteUnitOfWork wuow(opCtx); // There are no concurrent users of the index while --repair is running, so it is OK to // pass in a nullptr for the index 'ident', promising that the index is not in use. - catalog::removeIndex(opCtx, - indexName, - collection->getCatalogId(), - collection->uuid(), - collection->ns(), - nullptr /*ident */); + catalog::removeIndex(opCtx, indexName, collection, nullptr /*ident */); wuow.commit(); StorageRepairObserver::get(opCtx->getServiceContext()) diff --git a/src/mongo/db/repl/collection_bulk_loader_impl.cpp b/src/mongo/db/repl/collection_bulk_loader_impl.cpp index 903f2d3e692..ed2c1aa4233 100644 --- a/src/mongo/db/repl/collection_bulk_loader_impl.cpp +++ b/src/mongo/db/repl/collection_bulk_loader_impl.cpp @@ -87,8 +87,8 @@ Status CollectionBulkLoaderImpl::init(const std::vector<BSONObj>& secondaryIndex // This enforces the buildIndexes setting in the replica set configuration. CollectionWriter collWriter(*_collection); auto indexCatalog = collWriter.getWritableCollection()->getIndexCatalog(); - auto specs = - indexCatalog->removeExistingIndexesNoChecks(_opCtx.get(), secondaryIndexSpecs); + auto specs = indexCatalog->removeExistingIndexesNoChecks( + _opCtx.get(), collWriter.get(), secondaryIndexSpecs); if (specs.size()) { _secondaryIndexesBlock->ignoreUniqueConstraint(); auto status = diff --git a/src/mongo/db/repl/dbcheck.cpp b/src/mongo/db/repl/dbcheck.cpp index cd46bf05173..3a2f7d0db1f 100644 --- a/src/mongo/db/repl/dbcheck.cpp +++ b/src/mongo/db/repl/dbcheck.cpp @@ -43,7 +43,6 @@ #include "mongo/db/repl/dbcheck_gen.h" #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/optime.h" -#include "mongo/db/storage/durable_catalog.h" namespace mongo { @@ -355,12 +354,11 @@ std::vector<BSONObj> collectionIndexInfo(OperationContext* opCtx, const Collecti std::vector<std::string> names; // List the indices, - auto durableCatalog = DurableCatalog::get(opCtx); - durableCatalog->getAllIndexes(opCtx, collection->getCatalogId(), &names); + collection->getAllIndexes(&names); // and get the info for each one. for (const auto& name : names) { - result.push_back(durableCatalog->getIndexSpec(opCtx, collection->getCatalogId(), name)); + result.push_back(collection->getIndexSpec(name)); } auto comp = std::make_unique<SimpleBSONObjComparator>(); @@ -371,9 +369,7 @@ std::vector<BSONObj> collectionIndexInfo(OperationContext* opCtx, const Collecti } BSONObj collectionOptions(OperationContext* opCtx, const CollectionPtr& collection) { - return DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .toBSON(); + return collection->getCollectionOptions().toBSON(); } AutoGetDbForDbCheck::AutoGetDbForDbCheck(OperationContext* opCtx, const NamespaceString& nss) diff --git a/src/mongo/db/repl/idempotency_test_fixture.cpp b/src/mongo/db/repl/idempotency_test_fixture.cpp index f265fecf08f..4a0891546f6 100644 --- a/src/mongo/db/repl/idempotency_test_fixture.cpp +++ b/src/mongo/db/repl/idempotency_test_fixture.cpp @@ -60,7 +60,6 @@ #include "mongo/db/repl/replication_consistency_markers_mock.h" #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/util/md5.hpp" namespace mongo { @@ -415,15 +414,12 @@ CollectionState IdempotencyTest::validate(const NamespaceString& nss) { std::string dataHash = computeDataHash(collection.getCollection()); - auto durableCatalog = DurableCatalog::get(_opCtx.get()); - auto collectionOptions = - durableCatalog->getCollectionOptions(_opCtx.get(), collection->getCatalogId()); + auto collectionOptions = collection->getCollectionOptions(); std::vector<std::string> allIndexes; BSONObjSet indexSpecs = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); - durableCatalog->getAllIndexes(_opCtx.get(), collection->getCatalogId(), &allIndexes); + collection->getAllIndexes(&allIndexes); for (auto const& index : allIndexes) { - indexSpecs.insert( - durableCatalog->getIndexSpec(_opCtx.get(), collection->getCatalogId(), index)); + indexSpecs.insert(collection->getIndexSpec(index)); } ASSERT_EQUALS(indexSpecs.size(), allIndexes.size()); diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 36f859ce77d..00c24dcc725 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -89,7 +89,6 @@ #include "mongo/db/service_context.h" #include "mongo/db/stats/counters.h" #include "mongo/db/stats/server_write_concern_metrics.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/storage_engine.h" #include "mongo/db/storage/storage_options.h" #include "mongo/db/transaction_participant.h" @@ -208,7 +207,8 @@ void createIndexForApplyOps(OperationContext* opCtx, << "; normalized index specs: " << BSON("normalSpecs" << normalSpecs)); auto indexCatalog = indexCollection->getIndexCatalog(); - auto prepareSpecResult = indexCatalog->prepareSpecForCreate(opCtx, normalSpecs[0], {}); + auto prepareSpecResult = + indexCatalog->prepareSpecForCreate(opCtx, indexCollection, normalSpecs[0], {}); if (ErrorCodes::IndexBuildAlreadyInProgress == prepareSpecResult) { LOGV2(4924900, "Index build: already in progress during initial sync", @@ -661,8 +661,7 @@ void createOplog(OperationContext* opCtx, if (collection) { if (replSettings.getOplogSizeBytes() != 0) { - const CollectionOptions oplogOpts = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + const CollectionOptions& oplogOpts = collection->getCollectionOptions(); int o = (int)(oplogOpts.cappedSize / (1024 * 1024)); int n = (int)(replSettings.getOplogSizeBytes() / (1024 * 1024)); diff --git a/src/mongo/db/repl/rollback_test_fixture.cpp b/src/mongo/db/repl/rollback_test_fixture.cpp index 4ae77c557ef..7b90135c649 100644 --- a/src/mongo/db/repl/rollback_test_fixture.cpp +++ b/src/mongo/db/repl/rollback_test_fixture.cpp @@ -47,7 +47,6 @@ #include "mongo/db/repl/replication_process.h" #include "mongo/db/repl/replication_recovery.h" #include "mongo/db/repl/rs_rollback.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/log_test.h" #include "mongo/util/str.h" @@ -356,9 +355,7 @@ void RollbackResyncsCollectionOptionsTest::resyncCollectionOptionsTest( // Make sure the collection options are correct. AutoGetCollectionForReadCommand autoColl(_opCtx.get(), NamespaceString(nss.toString())); - auto collAfterRollbackOptions = - DurableCatalog::get(_opCtx.get()) - ->getCollectionOptions(_opCtx.get(), autoColl.getCollection()->getCatalogId()); + auto collAfterRollbackOptions = autoColl->getCollectionOptions(); BSONObjBuilder expectedOptionsBob; if (localCollOptions.uuid) { diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index 87db2d9fe9f..a7099c5850f 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -74,7 +74,6 @@ #include "mongo/db/s/shard_identity_rollback_notifier.h" #include "mongo/db/session_catalog_mongod.h" #include "mongo/db/storage/control/journal_flusher.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/remove_saver.h" #include "mongo/db/transaction_participant.h" #include "mongo/logv2/log.h" @@ -856,9 +855,10 @@ void checkRbidAndUpdateMinValid(OperationContext* opCtx, * collection. */ void dropIndex(OperationContext* opCtx, - IndexCatalog* indexCatalog, + Collection* collection, const string& indexName, NamespaceString& nss) { + IndexCatalog* indexCatalog = collection->getIndexCatalog(); bool includeUnfinishedIndexes = true; auto indexDescriptor = indexCatalog->findIndexByName(opCtx, indexName, includeUnfinishedIndexes); @@ -872,8 +872,8 @@ void dropIndex(OperationContext* opCtx, } auto entry = indexCatalog->getEntry(indexDescriptor); - if (entry->isReady(opCtx)) { - auto status = indexCatalog->dropIndex(opCtx, indexDescriptor); + if (entry->isReady(opCtx, collection)) { + auto status = indexCatalog->dropIndex(opCtx, collection, indexDescriptor); if (!status.isOK()) { LOGV2_ERROR(21738, "Rollback failed to drop index {indexName} in {namespace}: {error}", @@ -883,7 +883,7 @@ void dropIndex(OperationContext* opCtx, "error"_attr = redact(status)); } } else { - auto status = indexCatalog->dropUnfinishedIndex(opCtx, indexDescriptor); + auto status = indexCatalog->dropUnfinishedIndex(opCtx, collection, indexDescriptor); if (!status.isOK()) { LOGV2_ERROR( 21739, @@ -945,7 +945,7 @@ void rollbackCreateIndexes(OperationContext* opCtx, UUID uuid, std::set<std::str "indexName"_attr = indexName); WriteUnitOfWork wuow(opCtx); - dropIndex(opCtx, collection.getWritableCollection()->getIndexCatalog(), indexName, *nss); + dropIndex(opCtx, collection.getWritableCollection(), indexName, *nss); wuow.commit(); LOGV2_DEBUG(21673, @@ -1620,7 +1620,7 @@ void rollback_internal::syncFixUp(OperationContext* opCtx, WriteUnitOfWork wuow(opCtx); // Set collection to whatever temp status is on the sync source. - DurableCatalog::get(opCtx)->setIsTemp(opCtx, collection->getCatalogId(), options.temp); + collection.getWritableCollection()->setIsTemp(opCtx, options.temp); // Set any document validation options. We update the validator fields without // parsing/validation, since we fetched the options object directly from the sync @@ -1645,10 +1645,7 @@ void rollback_internal::syncFixUp(OperationContext* opCtx, "namespace"_attr = *nss, "uuid"_attr = uuid, "info"_attr = redact(info), - "catalogId"_attr = - redact(DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, collection->getCatalogId()) - .toBSON())); + "catalogId"_attr = redact(collection->getCollectionOptions().toBSON())); } // Since we read from the sync source to retrieve the metadata of the diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp index b1ed5a19d95..ab900376345 100644 --- a/src/mongo/db/repl/rs_rollback_test.cpp +++ b/src/mongo/db/repl/rs_rollback_test.cpp @@ -58,7 +58,6 @@ #include "mongo/db/repl/rollback_test_fixture.h" #include "mongo/db/repl/rs_rollback.h" #include "mongo/db/s/shard_identity_rollback_notifier.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" #include "mongo/util/net/hostandport.h" @@ -213,7 +212,7 @@ int _createIndexOnEmptyCollection(OperationContext* opCtx, Lock::DBLock dbLock(opCtx, nss.db(), MODE_X); auto indexCatalog = coll->getIndexCatalog(); WriteUnitOfWork wunit(opCtx); - ASSERT_OK(indexCatalog->createIndexOnEmptyCollection(opCtx, indexSpec).getStatus()); + ASSERT_OK(indexCatalog->createIndexOnEmptyCollection(opCtx, coll, indexSpec).getStatus()); wunit.commit(); return indexCatalog->numIndexesReady(opCtx); } @@ -1358,9 +1357,7 @@ TEST_F(RSRollbackTest, RollbackRenameCollectionInSameDatabaseCommand) { ASSERT_TRUE(oldCollName.getCollection()); // Remote collection options should have been empty. - auto collAfterRollbackOptions = - DurableCatalog::get(_opCtx.get()) - ->getCollectionOptions(_opCtx.get(), oldCollName.getCollection()->getCatalogId()); + auto collAfterRollbackOptions = oldCollName->getCollectionOptions(); ASSERT_BSONOBJ_EQ(BSON("uuid" << *options.uuid), collAfterRollbackOptions.toBSON()); } } @@ -1416,9 +1413,7 @@ TEST_F(RSRollbackTest, ASSERT_TRUE(rollbackSource.getCollectionInfoCalled); AutoGetCollectionForReadCommand autoColl(_opCtx.get(), NamespaceString(renameFromNss)); - auto collAfterRollbackOptions = - DurableCatalog::get(_opCtx.get()) - ->getCollectionOptions(_opCtx.get(), autoColl.getCollection()->getCatalogId()); + auto collAfterRollbackOptions = autoColl->getCollectionOptions(); ASSERT_TRUE(collAfterRollbackOptions.temp); ASSERT_BSONOBJ_EQ(BSON("uuid" << *options.uuid << "temp" << true), collAfterRollbackOptions.toBSON()); @@ -2020,9 +2015,7 @@ TEST_F(RSRollbackTest, RollbackCollectionModificationCommand) { // Make sure the collection options are correct. AutoGetCollectionForReadCommand autoColl(_opCtx.get(), NamespaceString("test.t")); - auto collAfterRollbackOptions = - DurableCatalog::get(_opCtx.get()) - ->getCollectionOptions(_opCtx.get(), autoColl.getCollection()->getCatalogId()); + auto collAfterRollbackOptions = autoColl->getCollectionOptions(); ASSERT_BSONOBJ_EQ(BSON("uuid" << *options.uuid), collAfterRollbackOptions.toBSON()); } diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp index 67e991b944c..352f2799ecf 100644 --- a/src/mongo/db/repl/storage_interface_impl.cpp +++ b/src/mongo/db/repl/storage_interface_impl.cpp @@ -78,7 +78,6 @@ #include "mongo/db/storage/checkpointer.h" #include "mongo/db/storage/control/journal_flusher.h" #include "mongo/db/storage/control/storage_control.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/oplog_cap_maintainer_thread.h" #include "mongo/logv2/log.h" #include "mongo/util/assert_util.h" @@ -261,7 +260,8 @@ StorageInterfaceImpl::createCollectionForBulkLoading( if (!idIndexSpec.isEmpty()) { auto status = autoColl->getWritableCollection() ->getIndexCatalog() - ->createIndexOnEmptyCollection(opCtx.get(), idIndexSpec); + ->createIndexOnEmptyCollection( + opCtx.get(), autoColl->getWritableCollection(), idIndexSpec); if (!status.getStatus().isOK()) { return status.getStatus(); } @@ -269,7 +269,8 @@ StorageInterfaceImpl::createCollectionForBulkLoading( for (auto&& spec : secondaryIndexSpecs) { auto status = autoColl->getWritableCollection() ->getIndexCatalog() - ->createIndexOnEmptyCollection(opCtx.get(), spec); + ->createIndexOnEmptyCollection( + opCtx.get(), autoColl->getWritableCollection(), spec); if (!status.getStatus().isOK()) { return status.getStatus(); } @@ -454,26 +455,16 @@ Status StorageInterfaceImpl::createOplog(OperationContext* opCtx, const Namespac } StatusWith<size_t> StorageInterfaceImpl::getOplogMaxSize(OperationContext* opCtx) { - // This writeConflictRetry loop protects callers from WriteConflictExceptions thrown by the - // storage engine running out of cache space, despite this operation not performing any writes. - return writeConflictRetry( - opCtx, - "StorageInterfaceImpl::getOplogMaxSize", - NamespaceString::kRsOplogNamespace.ns(), - [&]() -> StatusWith<size_t> { - AutoGetOplog oplogRead(opCtx, OplogAccessMode::kRead); - const auto& oplog = oplogRead.getCollection(); - if (!oplog) { - return {ErrorCodes::NamespaceNotFound, "Your oplog doesn't exist."}; - } - const auto options = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, oplog->getCatalogId()); - if (!options.capped) - return {ErrorCodes::BadValue, - str::stream() - << NamespaceString::kRsOplogNamespace.ns() << " isn't capped"}; - return options.cappedSize; - }); + AutoGetOplog oplogRead(opCtx, OplogAccessMode::kRead); + const auto& oplog = oplogRead.getCollection(); + if (!oplog) { + return {ErrorCodes::NamespaceNotFound, "Your oplog doesn't exist."}; + } + const auto options = oplog->getCollectionOptions(); + if (!options.capped) + return {ErrorCodes::BadValue, + str::stream() << NamespaceString::kRsOplogNamespace.ns() << " isn't capped"}; + return options.cappedSize; } Status StorageInterfaceImpl::createCollection(OperationContext* opCtx, diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp index 5c12b016b12..7b854f41c72 100644 --- a/src/mongo/db/repl/storage_interface_impl_test.cpp +++ b/src/mongo/db/repl/storage_interface_impl_test.cpp @@ -52,7 +52,6 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/repl/storage_interface_impl.h" #include "mongo/db/service_context_d_test_fixture.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/stdx/thread.h" #include "mongo/transport/transport_layer_mock.h" #include "mongo/unittest/unittest.h" @@ -137,7 +136,9 @@ int _createIndexOnEmptyCollection(OperationContext* opCtx, NamespaceString nss, auto indexCatalog = coll.getWritableCollection()->getIndexCatalog(); ASSERT(indexCatalog); - ASSERT_OK(indexCatalog->createIndexOnEmptyCollection(opCtx, indexSpec).getStatus()); + ASSERT_OK( + indexCatalog->createIndexOnEmptyCollection(opCtx, coll.getWritableCollection(), indexSpec) + .getStatus()); wunit.commit(); return indexCatalog->numIndexesReady(opCtx); @@ -793,9 +794,7 @@ TEST_F(StorageInterfaceImplTest, RenameCollectionWithStayTempFalseMakesItNotTemp AutoGetCollectionForReadCommand autoColl2(opCtx, toNss); ASSERT_TRUE(autoColl2.getCollection()); - ASSERT_FALSE(DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, autoColl2.getCollection()->getCatalogId()) - .temp); + ASSERT_FALSE(autoColl2->getCollectionOptions().temp); } TEST_F(StorageInterfaceImplTest, RenameCollectionWithStayTempTrueMakesItTemp) { @@ -814,9 +813,7 @@ TEST_F(StorageInterfaceImplTest, RenameCollectionWithStayTempTrueMakesItTemp) { AutoGetCollectionForReadCommand autoColl2(opCtx, toNss); ASSERT_TRUE(autoColl2.getCollection()); - ASSERT_TRUE(DurableCatalog::get(opCtx) - ->getCollectionOptions(opCtx, autoColl2.getCollection()->getCatalogId()) - .temp); + ASSERT_TRUE(autoColl2->getCollectionOptions().temp); } TEST_F(StorageInterfaceImplTest, RenameCollectionFailsBetweenDatabases) { diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp index 584d3791bf2..33b1d94590b 100644 --- a/src/mongo/db/s/migration_destination_manager.cpp +++ b/src/mongo/db/s/migration_destination_manager.cpp @@ -808,7 +808,7 @@ void MigrationDestinationManager::cloneCollectionIndexesAndOptions( auto checkEmptyOrGetMissingIndexesFromDonor = [&](const CollectionPtr& collection) { auto indexCatalog = collection->getIndexCatalog(); auto indexSpecs = indexCatalog->removeExistingIndexesNoChecks( - opCtx, collectionOptionsAndIndexes.indexSpecs); + opCtx, collection, collectionOptionsAndIndexes.indexSpecs); if (!indexSpecs.empty()) { // Only allow indexes to be copied if the collection does not have any documents. uassert(ErrorCodes::CannotCreateCollection, diff --git a/src/mongo/db/s/shard_local.cpp b/src/mongo/db/s/shard_local.cpp index 873b628ad65..506e21ffbf4 100644 --- a/src/mongo/db/s/shard_local.cpp +++ b/src/mongo/db/s/shard_local.cpp @@ -167,6 +167,7 @@ Status ShardLocal::createIndexOnConfig(OperationContext* opCtx, auto removeIndexBuildsToo = false; auto indexSpecs = indexCatalog->removeExistingIndexes( opCtx, + CollectionPtr(collection, CollectionPtr::NoYieldTag{}), uassertStatusOK( collection->addCollationDefaultsToIndexSpecsForCreate(opCtx, {index.toBSON()})), removeIndexBuildsToo); diff --git a/src/mongo/db/startup_recovery.cpp b/src/mongo/db/startup_recovery.cpp index 74904339b81..d91d484d319 100644 --- a/src/mongo/db/startup_recovery.cpp +++ b/src/mongo/db/startup_recovery.cpp @@ -53,7 +53,6 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl_set_member_in_standalone_mode.h" #include "mongo/db/server_options.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/storage_repair_observer.h" #include "mongo/logv2/log.h" #include "mongo/util/exit.h" @@ -148,11 +147,10 @@ Status restoreMissingFeatureCompatibilityVersionDocument(OperationContext* opCtx * Returns true if the collection associated with the given CollectionCatalogEntry has an index on * the _id field */ -bool checkIdIndexExists(OperationContext* opCtx, RecordId catalogId) { - auto durableCatalog = DurableCatalog::get(opCtx); - auto indexCount = durableCatalog->getTotalIndexCount(opCtx, catalogId); +bool checkIdIndexExists(OperationContext* opCtx, const CollectionPtr& coll) { + auto indexCount = coll->getTotalIndexCount(); auto indexNames = std::vector<std::string>(indexCount); - durableCatalog->getAllIndexes(opCtx, catalogId, &indexNames); + coll->getAllIndexes(&indexNames); for (auto name : indexNames) { if (name == "_id_") { @@ -171,7 +169,7 @@ Status buildMissingIdIndex(OperationContext* opCtx, Collection* collection) { }); const auto indexCatalog = collection->getIndexCatalog(); - const auto idIndexSpec = indexCatalog->getDefaultIdIndexSpec(); + const auto idIndexSpec = indexCatalog->getDefaultIdIndexSpec(collection); CollectionWriter collWriter(collection); auto swSpecs = indexer.init(opCtx, collWriter, idIndexSpec, MultiIndexBlock::kNoopOnInitFn); @@ -225,15 +223,13 @@ Status ensureCollectionProperties(OperationContext* opCtx, // All user-created replicated collections created since MongoDB 4.0 have _id indexes. auto requiresIndex = coll->requiresIdIndex() && coll->ns().isReplicated(); - auto collOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, coll->getCatalogId()); + const auto& collOptions = coll->getCollectionOptions(); auto hasAutoIndexIdField = collOptions.autoIndexId == CollectionOptions::YES; // Even if the autoIndexId field is not YES, the collection may still have an _id index // that was created manually by the user. Check the list of indexes to confirm index // does not exist before attempting to build it or returning an error. - if (requiresIndex && !hasAutoIndexIdField && - !checkIdIndexExists(opCtx, coll->getCatalogId())) { + if (requiresIndex && !hasAutoIndexIdField && !checkIdIndexExists(opCtx, coll)) { LOGV2(21001, "collection {coll_ns} is missing an _id index", "Collection is missing an _id index", @@ -397,13 +393,13 @@ void reconcileCatalogAndRebuildUnfinishedIndexes( // Determine which indexes need to be rebuilt. rebuildIndexesOnCollection() requires that all // indexes on that collection are done at once, so we use a map to group them together. StringMap<IndexNameObjs> nsToIndexNameObjMap; + auto catalog = CollectionCatalog::get(opCtx); for (auto&& idxIdentifier : reconcileResult.indexesToRebuild) { NamespaceString collNss = idxIdentifier.nss; const std::string& indexName = idxIdentifier.indexName; auto swIndexSpecs = - getIndexNameObjs(opCtx, idxIdentifier.catalogId, [&indexName](const std::string& name) { - return name == indexName; - }); + getIndexNameObjs(catalog->lookupCollectionByNamespace(opCtx, collNss), + [&indexName](const std::string& name) { return name == indexName; }); if (!swIndexSpecs.isOK() || swIndexSpecs.getValue().first.empty()) { fassert(40590, {ErrorCodes::InternalError, @@ -420,7 +416,6 @@ void reconcileCatalogAndRebuildUnfinishedIndexes( ino.second.emplace_back(std::move(indexesToRebuild.second.back())); } - auto catalog = CollectionCatalog::get(opCtx); for (const auto& entry : nsToIndexNameObjMap) { NamespaceString collNss(entry.first); diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript index 710b48e82a6..4a2297efea8 100644 --- a/src/mongo/db/storage/SConscript +++ b/src/mongo/db/storage/SConscript @@ -466,6 +466,7 @@ env.CppUnitTest( LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/auth/authmocks', + '$BUILD_DIR/mongo/db/catalog/catalog_impl', '$BUILD_DIR/mongo/db/catalog/catalog_test_fixture', '$BUILD_DIR/mongo/db/catalog/collection_options', '$BUILD_DIR/mongo/db/catalog_raii', diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.cpp b/src/mongo/db/storage/bson_collection_catalog_entry.cpp index e63344740fd..5b949f23fff 100644 --- a/src/mongo/db/storage/bson_collection_catalog_entry.cpp +++ b/src/mongo/db/storage/bson_collection_catalog_entry.cpp @@ -160,7 +160,7 @@ bool BSONCollectionCatalogEntry::MetaData::eraseIndex(StringData name) { return true; } -BSONObj BSONCollectionCatalogEntry::MetaData::toBSON() const { +BSONObj BSONCollectionCatalogEntry::MetaData::toBSON(bool hasExclusiveAccess) const { BSONObjBuilder b; b.append("ns", ns); b.append("options", options.toBSON()); @@ -170,14 +170,20 @@ BSONObj BSONCollectionCatalogEntry::MetaData::toBSON() const { BSONObjBuilder sub(arr.subobjStart()); sub.append("spec", indexes[i].spec); sub.appendBool("ready", indexes[i].ready); - sub.appendBool("multikey", indexes[i].multikey); - - if (!indexes[i].multikeyPaths.empty()) { - BSONObjBuilder subMultikeyPaths(sub.subobjStart("multikeyPaths")); - appendMultikeyPathsAsBytes(indexes[i].spec.getObjectField("key"), - indexes[i].multikeyPaths, - &subMultikeyPaths); - subMultikeyPaths.doneFast(); + { + stdx::unique_lock lock(indexes[i].multikeyMutex, stdx::defer_lock_t{}); + if (!hasExclusiveAccess) { + lock.lock(); + } + sub.appendBool("multikey", indexes[i].multikey); + + if (!indexes[i].multikeyPaths.empty()) { + BSONObjBuilder subMultikeyPaths(sub.subobjStart("multikeyPaths")); + appendMultikeyPathsAsBytes(indexes[i].spec.getObjectField("key"), + indexes[i].multikeyPaths, + &subMultikeyPaths); + subMultikeyPaths.doneFast(); + } } sub.append("head", 0ll); // For backward compatibility with 4.0 diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.h b/src/mongo/db/storage/bson_collection_catalog_entry.h index c29e2146074..e8cee07e2f2 100644 --- a/src/mongo/db/storage/bson_collection_catalog_entry.h +++ b/src/mongo/db/storage/bson_collection_catalog_entry.h @@ -61,6 +61,30 @@ public: struct IndexMetaData { IndexMetaData() {} + IndexMetaData(const IndexMetaData& other) + : spec(other.spec), + ready(other.ready), + isBackgroundSecondaryBuild(other.isBackgroundSecondaryBuild), + buildUUID(other.buildUUID) { + // We need to hold the multikey mutex when copying, someone else might be modifying this + stdx::lock_guard lock(other.multikeyMutex); + multikey = other.multikey; + multikeyPaths = other.multikeyPaths; + } + + IndexMetaData& operator=(IndexMetaData&& rhs) { + spec = std::move(rhs.spec); + ready = std::move(rhs.ready); + isBackgroundSecondaryBuild = std::move(rhs.isBackgroundSecondaryBuild); + buildUUID = std::move(rhs.buildUUID); + + // No need to hold mutex on move, there are no concurrent readers while we're moving the + // instance. + multikey = std::move(rhs.multikey); + multikeyPaths = std::move(rhs.multikeyPaths); + return *this; + } + void updateTTLSetting(long long newExpireSeconds); void updateHiddenSetting(bool hidden); @@ -71,7 +95,6 @@ public: BSONObj spec; bool ready = false; - bool multikey = false; bool isBackgroundSecondaryBuild = false; // If initialized, a two-phase index build is in progress. @@ -81,12 +104,20 @@ public: // the index key pattern. Each element in the vector is an ordered set of positions // (starting at 0) into the corresponding indexed field that represent what prefixes of the // indexed field cause the index to be multikey. - MultikeyPaths multikeyPaths; + // multikeyMutex must be held when accessing multikey or multikeyPaths + mutable Mutex multikeyMutex; + mutable bool multikey = false; + mutable MultikeyPaths multikeyPaths; }; struct MetaData { void parse(const BSONObj& obj); - BSONObj toBSON() const; + + /** + * If we have exclusive access to this MetaData (holding a unique copy). We don't need to + * hold mutexes when reading internal data. + */ + BSONObj toBSON(bool hasExclusiveAccess = false) const; int findIndexOffset(StringData name) const; diff --git a/src/mongo/db/storage/durable_catalog.h b/src/mongo/db/storage/durable_catalog.h index f1935e66fad..5d6d24848b7 100644 --- a/src/mongo/db/storage/durable_catalog.h +++ b/src/mongo/db/storage/durable_catalog.h @@ -38,7 +38,6 @@ #include "mongo/db/storage/storage_engine.h" namespace mongo { - /** * An interface to modify the on-disk catalog metadata. */ @@ -82,8 +81,8 @@ public: virtual BSONObj getCatalogEntry(OperationContext* opCtx, RecordId catalogId) const = 0; - virtual BSONCollectionCatalogEntry::MetaData getMetaData(OperationContext* opCtx, - RecordId id) const = 0; + virtual std::shared_ptr<BSONCollectionCatalogEntry::MetaData> getMetaData( + OperationContext* opCtx, RecordId id) const = 0; /** * Updates the catalog entry for the collection 'nss' with the fields specified in 'md'. If @@ -94,14 +93,6 @@ public: RecordId id, BSONCollectionCatalogEntry::MetaData& md) = 0; - /** - * Checks that the metadata for the index exists and matches the given spec. - */ - virtual Status checkMetaDataForIndex(OperationContext* opCtx, - RecordId catalogId, - const std::string& indexName, - const BSONObj& spec) = 0; - virtual std::vector<std::string> getAllIdents(OperationContext* opCtx) const = 0; virtual bool isUserDataIdent(StringData ident) const = 0; @@ -110,6 +101,7 @@ public: virtual bool isCollectionIdent(StringData ident) const = 0; + virtual RecordStore* getRecordStore() = 0; /** @@ -143,6 +135,17 @@ public: const CollectionOptions& options, bool allocateDefaultSpace) = 0; + virtual Status createIndex(OperationContext* opCtx, + RecordId catalogId, + const CollectionOptions& collOptions, + const IndexDescriptor* spec) = 0; + + virtual BSONCollectionCatalogEntry::IndexMetaData prepareIndexMetaDataForIndexBuild( + OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild) = 0; + /** * Import a collection by inserting the given metadata into the durable catalog and instructing * the storage engine to import the corresponding idents. The metadata object should be a valid @@ -175,7 +178,7 @@ public: virtual Status renameCollection(OperationContext* opCtx, RecordId catalogId, const NamespaceString& toNss, - bool stayTemp) = 0; + BSONCollectionCatalogEntry::MetaData& md) = 0; /** * Deletes the persisted collection catalog entry identified by 'catalogId'. @@ -186,105 +189,22 @@ public: virtual Status dropCollection(OperationContext* opCtx, RecordId catalogId) = 0; /** - * Updates size of a capped Collection. - */ - virtual void updateCappedSize(OperationContext* opCtx, RecordId catalogId, long long size) = 0; - - /** - * Updates the expireAfterSeconds option on the clustered index. If no expireAfterSeconds value - * is passed in then TTL deletions will be stopped on the clustered index. - */ - virtual void updateClusteredIndexTTLSetting(OperationContext* opCtx, - RecordId catalogId, - boost::optional<int64_t> expireAfterSeconds) = 0; - - /* - * Updates the expireAfterSeconds field of the given index to the value in newExpireSecs. - * The specified index must already contain an expireAfterSeconds field, and the value in - * that field and newExpireSecs must both be numeric. - */ - virtual void updateTTLSetting(OperationContext* opCtx, - RecordId catalogId, - StringData idxName, - long long newExpireSeconds) = 0; - - /* - * Hide or unhide the given index. A hidden index will not be considered for use by the - * query planner. - */ - virtual void updateHiddenSetting(OperationContext* opCtx, - RecordId catalogId, - StringData idxName, - bool hidden) = 0; - - /** - * Compares the UUID argument to the UUID obtained from the metadata. Returns true if they are - * equal, false otherwise. - */ - virtual bool isEqualToMetadataUUID(OperationContext* opCtx, - RecordId catalogId, - const UUID& uuid) = 0; - - /** - * Updates the 'temp' setting for this collection. - */ - virtual void setIsTemp(OperationContext* opCtx, RecordId catalogId, bool isTemp) = 0; - - /** - * Updates whether updates/deletes should store their pre-images in the opLog. - */ - virtual void setRecordPreImages(OperationContext* opCtx, RecordId catalogId, bool val) = 0; - - /** - * Updates the validator for this collection. - * - * An empty validator removes all validation. - */ - virtual void updateValidator(OperationContext* opCtx, - RecordId catalogId, - const BSONObj& validator, - boost::optional<ValidationLevelEnum> newLevel, - boost::optional<ValidationActionEnum> newAction) = 0; - - /** - * Removes the index 'indexName' from the persisted collection catalog entry identified by - * 'catalogId'. - */ - virtual void removeIndex(OperationContext* opCtx, RecordId catalogId, StringData indexName) = 0; - - /** - * Updates the persisted catalog entry for 'ns' with the new index and creates the index on - * disk. - * - * A passed 'buildUUID' implies that the index is part of a two-phase index build. - */ - virtual Status prepareForIndexBuild(OperationContext* opCtx, - RecordId catalogId, - const IndexDescriptor* spec, - boost::optional<UUID> buildUUID, - bool isBackgroundSecondaryBuild) = 0; - - /** * Drops the provided ident and recreates it as empty for use in resuming an index build. */ virtual Status dropAndRecreateIndexIdentForResume(OperationContext* opCtx, - RecordId catalogId, + const CollectionOptions& collOptions, const IndexDescriptor* spec, StringData ident) = 0; - /** - * Returns a UUID if the index is being built with the two-phase index build procedure. - */ - virtual boost::optional<UUID> getIndexBuildUUID(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const = 0; + virtual int getTotalIndexCount(OperationContext* opCtx, RecordId catalogId) const = 0; - /** - * Indicate that an index build is completed and the index is ready to use. - */ - virtual void indexBuildSuccess(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) = 0; + virtual bool isIndexPresent(OperationContext* opCtx, + RecordId catalogId, + StringData indexName) const = 0; + + virtual bool isIndexReady(OperationContext* opCtx, + RecordId catalogId, + StringData indexName) const = 0; /** * Returns true if the index identified by 'indexName' is multikey, and returns false otherwise. @@ -302,62 +222,6 @@ public: StringData indexName, MultikeyPaths* multikeyPaths) const = 0; - /** - * Sets the index identified by 'indexName' to be multikey. - * - * If 'multikeyPaths' is non-empty, then it must be a vector with size equal to the number of - * elements in the index key pattern. Additionally, at least one path component of the indexed - * fields must cause this index to be multikey. - * - * This function returns true if the index metadata has changed, and returns false otherwise. - */ - virtual bool setIndexIsMultikey(OperationContext* opCtx, - RecordId catalogId, - StringData indexName, - const MultikeyPaths& multikeyPaths) = 0; - - /** - * Sets the index to be multikey with the provided paths. This performs minimal validation of - * the inputs and is intended to be used internally to "correct" multikey metadata that drifts - * from the underlying collection data. - * - * When isMultikey is false, ignores multikeyPaths and resets the metadata appropriately based - * on the index descriptor. Otherwise, overwrites the existing multikeyPaths with the ones - * provided. This only writes multikey paths if the index type supports path-level tracking, and - * only sets the multikey boolean flag otherwise. - */ - virtual void forceSetIndexIsMultikey(OperationContext* opCtx, - RecordId catalogId, - const IndexDescriptor* desc, - bool isMultikey, - const MultikeyPaths& multikeyPaths) = 0; - - virtual CollectionOptions getCollectionOptions(OperationContext* opCtx, - RecordId catalogId) const = 0; - - virtual int getTotalIndexCount(OperationContext* opCtx, RecordId catalogId) const = 0; - - virtual int getCompletedIndexCount(OperationContext* opCtx, RecordId catalogId) const = 0; - - virtual BSONObj getIndexSpec(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const = 0; - - virtual void getAllIndexes(OperationContext* opCtx, - RecordId catalogId, - std::vector<std::string>* names) const = 0; - - virtual void getReadyIndexes(OperationContext* opCtx, - RecordId catalogId, - std::vector<std::string>* names) const = 0; - - virtual bool isIndexPresent(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const = 0; - - virtual bool isIndexReady(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const = 0; virtual void setRand_forTest(const std::string& rand) = 0; diff --git a/src/mongo/db/storage/durable_catalog_impl.cpp b/src/mongo/db/storage/durable_catalog_impl.cpp index cbc354ee6e8..86ed33795fa 100644 --- a/src/mongo/db/storage/durable_catalog_impl.cpp +++ b/src/mongo/db/storage/durable_catalog_impl.cpp @@ -604,15 +604,16 @@ BSONObj DurableCatalogImpl::_findEntry(OperationContext* opCtx, RecordId catalog return data.releaseToBson().getOwned(); } -BSONCollectionCatalogEntry::MetaData DurableCatalogImpl::getMetaData(OperationContext* opCtx, - RecordId catalogId) const { +std::shared_ptr<BSONCollectionCatalogEntry::MetaData> DurableCatalogImpl::getMetaData( + OperationContext* opCtx, RecordId catalogId) const { BSONObj obj = _findEntry(opCtx, catalogId); LOGV2_DEBUG(22209, 3, " fetched CCE metadata: {obj}", "obj"_attr = obj); - BSONCollectionCatalogEntry::MetaData md; + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> md; const BSONElement mdElement = obj["md"]; if (mdElement.isABSONObj()) { LOGV2_DEBUG(22210, 3, "returning metadata: {mdElement}", "mdElement"_attr = mdElement); - md.parse(mdElement.Obj()); + md = std::make_shared<BSONCollectionCatalogEntry::MetaData>(); + md->parse(mdElement.Obj()); } return md; } @@ -638,7 +639,7 @@ void DurableCatalogImpl::putMetaData(OperationContext* opCtx, string name = index.name(); // All indexes with buildUUIDs must be ready:false. - invariant(!(index.buildUUID && index.ready), str::stream() << md.toBSON()); + invariant(!(index.buildUUID && index.ready), str::stream() << md.toBSON(true)); // fix ident map BSONElement e = oldIdentMap[name]; @@ -665,44 +666,15 @@ void DurableCatalogImpl::putMetaData(OperationContext* opCtx, fassert(28521, status); } -Status DurableCatalogImpl::checkMetaDataForIndex(OperationContext* opCtx, - RecordId catalogId, - const std::string& indexName, - const BSONObj& spec) { - auto md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(indexName); - if (offset < 0) { - return {ErrorCodes::IndexNotFound, - str::stream() << "Index [" << indexName - << "] not found in metadata for recordId: " << catalogId}; - } - - if (spec.woCompare(md.indexes[offset].spec)) { - return {ErrorCodes::BadValue, - str::stream() << "Spec for index [" << indexName - << "] does not match spec in the metadata for recordId: " << catalogId - << ". Spec: " << spec - << " metadata's spec: " << md.indexes[offset].spec}; - } - - return Status::OK(); -} - Status DurableCatalogImpl::_replaceEntry(OperationContext* opCtx, RecordId catalogId, const NamespaceString& toNss, - bool stayTemp) { + BSONCollectionCatalogEntry::MetaData& md) { BSONObj old = _findEntry(opCtx, catalogId).getOwned(); { BSONObjBuilder b; b.append("ns", toNss.ns()); - - BSONCollectionCatalogEntry::MetaData md; - md.parse(old["md"].Obj()); - md.ns = toNss.ns(); - if (!stayTemp) - md.options.temp = false; b.append("md", md.toBSON()); b.appendElementsUnique(old); @@ -887,6 +859,21 @@ StatusWith<std::pair<RecordId, std::unique_ptr<RecordStore>>> DurableCatalogImpl return std::pair<RecordId, std::unique_ptr<RecordStore>>(entry.catalogId, std::move(rs)); } +Status DurableCatalogImpl::createIndex(OperationContext* opCtx, + RecordId catalogId, + const CollectionOptions& collOptions, + const IndexDescriptor* spec) { + std::string ident = getIndexIdent(opCtx, catalogId, spec->indexName()); + + auto kvEngine = _engine->getEngine(); + const Status status = kvEngine->createSortedDataInterface(opCtx, collOptions, ident, spec); + if (status.isOK()) { + opCtx->recoveryUnit()->registerChange( + std::make_unique<AddIndexChange>(opCtx->recoveryUnit(), _engine, ident)); + } + return status; +} + StatusWith<DurableCatalog::ImportResult> DurableCatalogImpl::importCollection( OperationContext* opCtx, const NamespaceString& nss, @@ -996,8 +983,8 @@ StatusWith<DurableCatalog::ImportResult> DurableCatalogImpl::importCollection( Status DurableCatalogImpl::renameCollection(OperationContext* opCtx, RecordId catalogId, const NamespaceString& toNss, - bool stayTemp) { - return _replaceEntry(opCtx, catalogId, toNss, stayTemp); + BSONCollectionCatalogEntry::MetaData& md) { + return _replaceEntry(opCtx, catalogId, toNss, md); } Status DurableCatalogImpl::dropCollection(OperationContext* opCtx, RecordId catalogId) { @@ -1019,102 +1006,11 @@ Status DurableCatalogImpl::dropCollection(OperationContext* opCtx, RecordId cata return Status::OK(); } -void DurableCatalogImpl::updateCappedSize(OperationContext* opCtx, - RecordId catalogId, - long long size) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - md.options.cappedSize = size; - putMetaData(opCtx, catalogId, md); -} - -void DurableCatalogImpl::updateClusteredIndexTTLSetting( - OperationContext* opCtx, RecordId catalogId, boost::optional<int64_t> expireAfterSeconds) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - uassert(5401000, "The collection doesn't have a clustered index", md.options.clusteredIndex); - - md.options.clusteredIndex->setExpireAfterSeconds(expireAfterSeconds); - putMetaData(opCtx, catalogId, md); -} - -void DurableCatalogImpl::updateTTLSetting(OperationContext* opCtx, - RecordId catalogId, - StringData idxName, - long long newExpireSeconds) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(idxName); - invariant(offset >= 0, - str::stream() << "cannot update TTL setting for index " << idxName << " @ " - << catalogId << " : " << md.toBSON()); - md.indexes[offset].updateTTLSetting(newExpireSeconds); - putMetaData(opCtx, catalogId, md); -} - -void DurableCatalogImpl::updateHiddenSetting(OperationContext* opCtx, - RecordId catalogId, - StringData idxName, - bool hidden) { - - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(idxName); - invariant(offset >= 0); - md.indexes[offset].updateHiddenSetting(hidden); - putMetaData(opCtx, catalogId, md); -} - - -bool DurableCatalogImpl::isEqualToMetadataUUID(OperationContext* opCtx, - RecordId catalogId, - const UUID& uuid) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - invariant(md.options.uuid, - str::stream() << "UUID missing for catalog entry " << catalogId << " : " - << md.toBSON()); - return *md.options.uuid == uuid; -} - -void DurableCatalogImpl::setIsTemp(OperationContext* opCtx, RecordId catalogId, bool isTemp) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - md.options.temp = isTemp; - putMetaData(opCtx, catalogId, md); -} - -void DurableCatalogImpl::setRecordPreImages(OperationContext* opCtx, RecordId catalogId, bool val) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - md.options.recordPreImages = val; - putMetaData(opCtx, catalogId, md); -} - -void DurableCatalogImpl::updateValidator(OperationContext* opCtx, - RecordId catalogId, - const BSONObj& validator, - boost::optional<ValidationLevelEnum> newLevel, - boost::optional<ValidationActionEnum> newAction) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - md.options.validator = validator; - md.options.validationLevel = newLevel; - md.options.validationAction = newAction; - putMetaData(opCtx, catalogId, md); -} - -void DurableCatalogImpl::removeIndex(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - if (md.findIndexOffset(indexName) < 0) - return; // never had the index so nothing to do. - - md.eraseIndex(indexName); - putMetaData(opCtx, catalogId, md); -} - -Status DurableCatalogImpl::prepareForIndexBuild(OperationContext* opCtx, - RecordId catalogId, - const IndexDescriptor* spec, - boost::optional<UUID> buildUUID, - bool isBackgroundSecondaryBuild) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - +BSONCollectionCatalogEntry::IndexMetaData DurableCatalogImpl::prepareIndexMetaDataForIndexBuild( + OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild) { BSONCollectionCatalogEntry::IndexMetaData imd; imd.spec = spec->infoObj(); imd.ready = false; @@ -1132,245 +1028,77 @@ Status DurableCatalogImpl::prepareForIndexBuild(OperationContext* opCtx, // Mark collation feature as in use if the index has a non-simple collation. if (imd.spec["collation"]) { - const auto feature = DurableCatalogImpl::FeatureTracker::NonRepairableFeature::kCollation; + const auto feature = FeatureTracker::NonRepairableFeature::kCollation; if (!getFeatureTracker()->isNonRepairableFeatureInUse(opCtx, feature)) { getFeatureTracker()->markNonRepairableFeatureAsInUse(opCtx, feature); } } - // Confirm that our index is not already in the current metadata. - invariant(-1 == md.findIndexOffset(imd.name())); - - md.indexes.push_back(imd); - putMetaData(opCtx, catalogId, md); - - string ident = getIndexIdent(opCtx, catalogId, spec->indexName()); - - auto kvEngine = _engine->getEngine(); - const Status status = kvEngine->createSortedDataInterface( - opCtx, getCollectionOptions(opCtx, catalogId), ident, spec); - if (status.isOK()) { - opCtx->recoveryUnit()->registerChange( - std::make_unique<AddIndexChange>(opCtx->recoveryUnit(), _engine, ident)); - } - - return status; + return imd; } Status DurableCatalogImpl::dropAndRecreateIndexIdentForResume(OperationContext* opCtx, - RecordId catalogId, + const CollectionOptions& collOptions, const IndexDescriptor* spec, StringData ident) { auto status = _engine->getEngine()->dropSortedDataInterface(opCtx, ident); if (!status.isOK()) return status; - status = _engine->getEngine()->createSortedDataInterface( - opCtx, getCollectionOptions(opCtx, catalogId), ident, spec); + status = _engine->getEngine()->createSortedDataInterface(opCtx, collOptions, ident, spec); return status; } -boost::optional<UUID> DurableCatalogImpl::getIndexBuildUUID(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(indexName); - invariant(offset >= 0, - str::stream() << "cannot get build UUID for index " << indexName << " @ " << catalogId - << " : " << md.toBSON()); - return md.indexes[offset].buildUUID; -} - -void DurableCatalogImpl::indexBuildSuccess(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(indexName); - invariant(offset >= 0, - str::stream() << "cannot mark index " << indexName << " as ready @ " << catalogId - << " : " << md.toBSON()); - md.indexes[offset].ready = true; - md.indexes[offset].buildUUID = boost::none; - putMetaData(opCtx, catalogId, md); -} - bool DurableCatalogImpl::isIndexMultikey(OperationContext* opCtx, RecordId catalogId, StringData indexName, MultikeyPaths* multikeyPaths) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); + auto md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(indexName); + int offset = md->findIndexOffset(indexName); invariant(offset >= 0, str::stream() << "cannot get multikey for index " << indexName << " @ " << catalogId - << " : " << md.toBSON()); - - if (multikeyPaths && !md.indexes[offset].multikeyPaths.empty()) { - *multikeyPaths = md.indexes[offset].multikeyPaths; - } - - return md.indexes[offset].multikey; -} - -bool DurableCatalogImpl::setIndexIsMultikey(OperationContext* opCtx, - RecordId catalogId, - StringData indexName, - const MultikeyPaths& multikeyPaths) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - int offset = md.findIndexOffset(indexName); - invariant(offset >= 0, - str::stream() << "cannot set index " << indexName << " as multikey @ " << catalogId - << " : " << md.toBSON()); - - const bool tracksPathLevelMultikeyInfo = !md.indexes[offset].multikeyPaths.empty(); - if (tracksPathLevelMultikeyInfo) { - invariant(!multikeyPaths.empty()); - invariant(multikeyPaths.size() == md.indexes[offset].multikeyPaths.size()); - } else { - invariant(multikeyPaths.empty()); - - if (md.indexes[offset].multikey) { - // The index is already set as multikey and we aren't tracking path-level multikey - // information for it. We return false to indicate that the index metadata is unchanged. - return false; - } - } - - md.indexes[offset].multikey = true; - - if (tracksPathLevelMultikeyInfo) { - bool newPathIsMultikey = false; - bool somePathIsMultikey = false; - - // Store new path components that cause this index to be multikey in catalog's index - // metadata. - for (size_t i = 0; i < multikeyPaths.size(); ++i) { - MultikeyComponents& indexMultikeyComponents = md.indexes[offset].multikeyPaths[i]; - for (const auto multikeyComponent : multikeyPaths[i]) { - auto result = indexMultikeyComponents.insert(multikeyComponent); - newPathIsMultikey = newPathIsMultikey || result.second; - somePathIsMultikey = true; - } - } - - // If all of the sets in the multikey paths vector were empty, then no component of any - // indexed field caused the index to be multikey. setIndexIsMultikey() therefore shouldn't - // have been called. - invariant(somePathIsMultikey); - - if (!newPathIsMultikey) { - // We return false to indicate that the index metadata is unchanged. - return false; - } - } - - putMetaData(opCtx, catalogId, md); - return true; -} + << " : " << md->toBSON()); -void DurableCatalogImpl::forceSetIndexIsMultikey(OperationContext* opCtx, - RecordId catalogId, - const IndexDescriptor* desc, - bool isMultikey, - const MultikeyPaths& multikeyPaths) { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - int offset = md.findIndexOffset(desc->indexName()); - invariant(offset >= 0, - str::stream() << "cannot set index " << desc->indexName() << " multikey state @ " - << catalogId << " : " << md.toBSON()); - - md.indexes[offset].multikey = isMultikey; - if (indexTypeSupportsPathLevelMultikeyTracking(desc->getAccessMethodName())) { - if (isMultikey) { - md.indexes[offset].multikeyPaths = multikeyPaths; - } else { - md.indexes[offset].multikeyPaths = - MultikeyPaths{static_cast<size_t>(desc->keyPattern().nFields())}; - } + if (multikeyPaths && !md->indexes[offset].multikeyPaths.empty()) { + *multikeyPaths = md->indexes[offset].multikeyPaths; } - putMetaData(opCtx, catalogId, md); -} - -CollectionOptions DurableCatalogImpl::getCollectionOptions(OperationContext* opCtx, - RecordId catalogId) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - return md.options; + return md->indexes[offset].multikey; } int DurableCatalogImpl::getTotalIndexCount(OperationContext* opCtx, RecordId catalogId) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - return static_cast<int>(md.indexes.size()); -} - -int DurableCatalogImpl::getCompletedIndexCount(OperationContext* opCtx, RecordId catalogId) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - int num = 0; - for (unsigned i = 0; i < md.indexes.size(); i++) { - if (md.indexes[i].ready) - num++; - } - return num; -} - -BSONObj DurableCatalogImpl::getIndexSpec(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - int offset = md.findIndexOffset(indexName); - invariant(offset >= 0, - str::stream() << "cannot get index spec for " << indexName << " @ " << catalogId - << " : " << md.toBSON()); - - BSONObj spec = md.indexes[offset].spec.getOwned(); - return spec; -} - -void DurableCatalogImpl::getAllIndexes(OperationContext* opCtx, - RecordId catalogId, - std::vector<std::string>* names) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - - for (unsigned i = 0; i < md.indexes.size(); i++) { - names->push_back(md.indexes[i].spec["name"].String()); - } -} - -void DurableCatalogImpl::getReadyIndexes(OperationContext* opCtx, - RecordId catalogId, - std::vector<std::string>* names) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); + auto md = getMetaData(opCtx, catalogId); + if (!md) + return 0; - for (unsigned i = 0; i < md.indexes.size(); i++) { - if (md.indexes[i].ready) - names->push_back(md.indexes[i].spec["name"].String()); - } + return static_cast<int>(md->indexes.size()); } bool DurableCatalogImpl::isIndexPresent(OperationContext* opCtx, RecordId catalogId, StringData indexName) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); - int offset = md.findIndexOffset(indexName); + auto md = getMetaData(opCtx, catalogId); + if (!md) + return false; + + int offset = md->findIndexOffset(indexName); return offset >= 0; } bool DurableCatalogImpl::isIndexReady(OperationContext* opCtx, RecordId catalogId, StringData indexName) const { - BSONCollectionCatalogEntry::MetaData md = getMetaData(opCtx, catalogId); + auto md = getMetaData(opCtx, catalogId); + if (!md) + return false; - int offset = md.findIndexOffset(indexName); + int offset = md->findIndexOffset(indexName); invariant(offset >= 0, str::stream() << "cannot get ready status for index " << indexName << " @ " - << catalogId << " : " << md.toBSON()); - return md.indexes[offset].ready; + << catalogId << " : " << md->toBSON()); + return md->indexes[offset].ready; } void DurableCatalogImpl::setRand_forTest(const std::string& rand) { diff --git a/src/mongo/db/storage/durable_catalog_impl.h b/src/mongo/db/storage/durable_catalog_impl.h index e28424e4ea3..63a8c9ec062 100644 --- a/src/mongo/db/storage/durable_catalog_impl.h +++ b/src/mongo/db/storage/durable_catalog_impl.h @@ -50,7 +50,6 @@ class StorageEngineInterface; class DurableCatalogImpl : public DurableCatalog { public: class FeatureTracker; - /** * The RecordStore must be thread-safe, in particular with concurrent calls to * RecordStore::find, updateRecord, insertRecord, deleteRecord and dataFor. The @@ -78,17 +77,12 @@ public: return _findEntry(opCtx, catalogId); } - BSONCollectionCatalogEntry::MetaData getMetaData(OperationContext* opCtx, - RecordId catalogId) const; + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> getMetaData(OperationContext* opCtx, + RecordId catalogId) const; void putMetaData(OperationContext* opCtx, RecordId catalogId, BSONCollectionCatalogEntry::MetaData& md); - Status checkMetaDataForIndex(OperationContext* opCtx, - RecordId catalogId, - const std::string& indexName, - const BSONObj& spec); - std::vector<std::string> getAllIdents(OperationContext* opCtx) const; bool isUserDataIdent(StringData ident) const; @@ -121,6 +115,17 @@ public: const CollectionOptions& options, bool allocateDefaultSpace); + Status createIndex(OperationContext* opCtx, + RecordId catalogId, + const CollectionOptions& collOptions, + const IndexDescriptor* spec); + + BSONCollectionCatalogEntry::IndexMetaData prepareIndexMetaDataForIndexBuild( + OperationContext* opCtx, + const IndexDescriptor* spec, + boost::optional<UUID> buildUUID, + bool isBackgroundSecondaryBuild); + StatusWith<ImportResult> importCollection(OperationContext* opCtx, const NamespaceString& nss, const BSONObj& metadata, @@ -130,89 +135,22 @@ public: Status renameCollection(OperationContext* opCtx, RecordId catalogId, const NamespaceString& toNss, - bool stayTemp); + BSONCollectionCatalogEntry::MetaData& md); Status dropCollection(OperationContext* opCtx, RecordId catalogId); - void updateCappedSize(OperationContext* opCtx, RecordId catalogId, long long size); - - void updateClusteredIndexTTLSetting(OperationContext* opCtx, - RecordId catalogId, - boost::optional<int64_t> expireAfterSeconds); - - void updateTTLSetting(OperationContext* opCtx, - RecordId catalogId, - StringData idxName, - long long newExpireSeconds); - - void updateHiddenSetting(OperationContext* opCtx, - RecordId catalogId, - StringData idxName, - bool hidden); - - bool isEqualToMetadataUUID(OperationContext* opCtx, RecordId catalogId, const UUID& uuid); - - void setIsTemp(OperationContext* opCtx, RecordId catalogId, bool isTemp); - - void setRecordPreImages(OperationContext* opCtx, RecordId catalogId, bool val) override; - - void updateValidator(OperationContext* opCtx, - RecordId catalogId, - const BSONObj& validator, - boost::optional<ValidationLevelEnum> newLevel, - boost::optional<ValidationActionEnum> newAction); - - void removeIndex(OperationContext* opCtx, RecordId catalogId, StringData indexName); - - Status prepareForIndexBuild(OperationContext* opCtx, - RecordId catalogId, - const IndexDescriptor* spec, - boost::optional<UUID> buildUUID, - bool isBackgroundSecondaryBuild); - Status dropAndRecreateIndexIdentForResume(OperationContext* opCtx, - RecordId catalogId, + const CollectionOptions& collOptions, const IndexDescriptor* spec, StringData ident); - boost::optional<UUID> getIndexBuildUUID(OperationContext* opCtx, - RecordId catalogId, - StringData indexName) const; - - void indexBuildSuccess(OperationContext* opCtx, RecordId catalogId, StringData indexName); - bool isIndexMultikey(OperationContext* opCtx, RecordId catalogId, StringData indexName, MultikeyPaths* multikeyPaths) const; - bool setIndexIsMultikey(OperationContext* opCtx, - RecordId catalogId, - StringData indexName, - const MultikeyPaths& multikeyPaths); - - void forceSetIndexIsMultikey(OperationContext* opCtx, - RecordId catalogId, - const IndexDescriptor* desc, - bool isMultikey, - const MultikeyPaths& multikeyPaths); - - CollectionOptions getCollectionOptions(OperationContext* opCtx, RecordId catalogId) const; - int getTotalIndexCount(OperationContext* opCtx, RecordId catalogId) const; - int getCompletedIndexCount(OperationContext* opCtx, RecordId catalogId) const; - - BSONObj getIndexSpec(OperationContext* opCtx, RecordId catalogId, StringData indexName) const; - - void getAllIndexes(OperationContext* opCtx, - RecordId catalogId, - std::vector<std::string>* names) const; - - void getReadyIndexes(OperationContext* opCtx, - RecordId catalogId, - std::vector<std::string>* names) const; - bool isIndexPresent(OperationContext* opCtx, RecordId catalogId, StringData indexName) const; bool isIndexReady(OperationContext* opCtx, RecordId catalogId, StringData indexName) const; @@ -240,7 +178,7 @@ private: Status _replaceEntry(OperationContext* opCtx, RecordId catalogId, const NamespaceString& toNss, - bool stayTemp); + BSONCollectionCatalogEntry::MetaData& md); Status _removeEntry(OperationContext* opCtx, RecordId catalogId); /** diff --git a/src/mongo/db/storage/kv/durable_catalog_test.cpp b/src/mongo/db/storage/kv/durable_catalog_test.cpp index d352e742728..595acc34bb1 100644 --- a/src/mongo/db/storage/kv/durable_catalog_test.cpp +++ b/src/mongo/db/storage/kv/durable_catalog_test.cpp @@ -35,7 +35,8 @@ #include "mongo/db/catalog/catalog_test_fixture.h" #include "mongo/db/catalog/collection_catalog.h" -#include "mongo/db/catalog/collection_mock.h" +#include "mongo/db/catalog/collection_impl.h" +#include "mongo/db/catalog_raii.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/multikey_paths.h" #include "mongo/db/index_names.h" @@ -63,7 +64,7 @@ public: CatalogTestFixture::setUp(); _nss = NamespaceString("unittests.durable_catalog"); - _catalogId = createCollection(_nss); + _collectionUUID = createCollection(_nss); } NamespaceString ns() { @@ -74,11 +75,17 @@ public: return operationContext()->getServiceContext()->getStorageEngine()->getCatalog(); } - RecordId getCatalogId() { - return _catalogId; + CollectionPtr getCollection() { + return CollectionCatalog::get(operationContext()) + ->lookupCollectionByUUID(operationContext(), *_collectionUUID); } - RecordId createCollection(const NamespaceString& nss) { + CollectionWriter getCollectionWriter() { + return CollectionWriter( + operationContext(), *_collectionUUID, CollectionCatalog::LifetimeMode::kInplace); + } + + CollectionUUID createCollection(const NamespaceString& nss) { Lock::DBLock dbLk(operationContext(), nss.db(), MODE_IX); Lock::CollectionLock collLk(operationContext(), nss, MODE_IX); @@ -95,7 +102,12 @@ public: std::pair<RecordId, std::unique_ptr<RecordStore>> coll = std::move(swColl.getValue()); RecordId catalogId = coll.first; - std::shared_ptr<Collection> collection = std::make_shared<CollectionMock>(nss, catalogId); + std::shared_ptr<Collection> collection = std::make_shared<CollectionImpl>( + operationContext(), + nss, + catalogId, + getCatalog()->getMetaData(operationContext(), catalogId), + std::move(coll.second)); CollectionCatalog::write(operationContext(), [&](CollectionCatalog& catalog) { catalog.registerCollection( operationContext(), options.uuid.get(), std::move(collection)); @@ -103,29 +115,47 @@ public: wuow.commit(); - return catalogId; + return *options.uuid; } - std::string createIndex(BSONObj keyPattern, - std::string indexType = IndexNames::BTREE, - bool twoPhase = false) { + IndexCatalogEntry* createIndex(BSONObj keyPattern, + std::string indexType = IndexNames::BTREE, + bool twoPhase = false) { + Lock::DBLock dbLk(operationContext(), _nss.db(), MODE_IX); + Lock::CollectionLock collLk(operationContext(), _nss, MODE_X); + std::string indexName = "idx" + std::to_string(numIndexesCreated); + // Make sure we have a valid IndexSpec for the type requested + IndexSpec spec; + spec.version(1).name(indexName).addKeys(keyPattern); + if (indexType == IndexNames::GEO_HAYSTACK) { + spec.geoHaystackBucketSize(1.0); + } else if (indexType == IndexNames::TEXT) { + spec.textWeights(BSON("a" << 1)); + spec.textIndexVersion(2); + spec.textDefaultLanguage("swedish"); + } - auto collection = std::make_unique<CollectionMock>(_nss); - IndexDescriptor desc(indexType, - BSON("v" << 1 << "key" << keyPattern << "name" << indexName)); + auto desc = std::make_unique<IndexDescriptor>(indexType, spec.toBSON()); + IndexCatalogEntry* entry = nullptr; + auto collWriter = getCollectionWriter(); { WriteUnitOfWork wuow(operationContext()); const bool isSecondaryBackgroundIndexBuild = false; boost::optional<UUID> buildUUID(twoPhase, UUID::gen()); - ASSERT_OK(getCatalog()->prepareForIndexBuild( - operationContext(), _catalogId, &desc, buildUUID, isSecondaryBackgroundIndexBuild)); + ASSERT_OK(collWriter.getWritableCollection()->prepareForIndexBuild( + operationContext(), desc.get(), buildUUID, isSecondaryBackgroundIndexBuild)); + entry = collWriter.getWritableCollection()->getIndexCatalog()->createIndexEntry( + operationContext(), + collWriter.getWritableCollection(), + std::move(desc), + CreateIndexEntryFlags::kNone); wuow.commit(); } ++numIndexesCreated; - return indexName; + return entry; } void assertMultikeyPathsAreEqual(const MultikeyPaths& actual, const MultikeyPaths& expected) { @@ -175,149 +205,156 @@ private: NamespaceString _nss; size_t numIndexesCreated = 0; - RecordId _catalogId; + // RecordId _catalogId; + OptionalCollectionUUID _collectionUUID; }; TEST_F(DurableCatalogTest, MultikeyPathsForBtreeIndexInitializedToVectorOfEmptySets) { - std::string indexName = createIndex(BSON("a" << 1 << "b" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1 << "b" << 1)); + auto collection = getCollection(); { MultikeyPaths multikeyPaths; - ASSERT(!catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(!collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {MultikeyComponents{}, MultikeyComponents{}}); } } TEST_F(DurableCatalogTest, CanSetIndividualPathComponentOfBtreeIndexAsMultikey) { - std::string indexName = createIndex(BSON("a" << 1 << "b" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1 << "b" << 1)); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, {MultikeyComponents{}, {0U}})); + ASSERT(collection->setIndexIsMultikey(operationContext(), + indexEntry->descriptor()->indexName(), + {MultikeyComponents{}, {0U}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {MultikeyComponents{}, {0U}}); } } TEST_F(DurableCatalogTest, MultikeyPathsAccumulateOnDifferentFields) { - std::string indexName = createIndex(BSON("a" << 1 << "b" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1 << "b" << 1)); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, {MultikeyComponents{}, {0U}})); + ASSERT(collection->setIndexIsMultikey(operationContext(), + indexEntry->descriptor()->indexName(), + {MultikeyComponents{}, {0U}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {MultikeyComponents{}, {0U}}); } { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, {{0U}, MultikeyComponents{}})); + ASSERT(collection->setIndexIsMultikey(operationContext(), + indexEntry->descriptor()->indexName(), + {{0U}, MultikeyComponents{}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {{0U}, {0U}}); } } TEST_F(DurableCatalogTest, MultikeyPathsAccumulateOnDifferentComponentsOfTheSameField) { - std::string indexName = createIndex(BSON("a.b" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a.b" << 1)); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey(operationContext(), getCatalogId(), indexName, {{0U}})); + ASSERT(collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), {{0U}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {{0U}}); } { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey(operationContext(), getCatalogId(), indexName, {{1U}})); + ASSERT(collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), {{1U}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {{0U, 1U}}); } } TEST_F(DurableCatalogTest, NoOpWhenSpecifiedPathComponentsAlreadySetAsMultikey) { - std::string indexName = createIndex(BSON("a" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1)); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey(operationContext(), getCatalogId(), indexName, {{0U}})); + ASSERT(collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), {{0U}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {{0U}}); } { WriteUnitOfWork wuow(operationContext()); - ASSERT(!catalog->setIndexIsMultikey(operationContext(), getCatalogId(), indexName, {{0U}})); + ASSERT(!collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), {{0U}})); // Rollback WUOW. } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {{0U}}); } } TEST_F(DurableCatalogTest, CanSetMultipleFieldsAndComponentsAsMultikey) { - std::string indexName = createIndex(BSON("a.b.c" << 1 << "a.b.d" << 1)); - DurableCatalog* catalog = getCatalog(); - + auto indexEntry = createIndex(BSON("a.b.c" << 1 << "a.b.d" << 1)); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, {{0U, 1U}, {0U, 1U}})); + ASSERT(collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), {{0U, 1U}, {0U, 1U}})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {{0U, 1U}, {0U, 1U}}); } } @@ -325,34 +362,35 @@ TEST_F(DurableCatalogTest, CanSetMultipleFieldsAndComponentsAsMultikey) { DEATH_TEST_REGEX_F(DurableCatalogTest, CannotOmitPathLevelMultikeyInfoWithBtreeIndex, R"#(Invariant failure.*!multikeyPaths.empty\(\))#") { - std::string indexName = createIndex(BSON("a" << 1 << "b" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1 << "b" << 1)); + auto collection = getCollection(); WriteUnitOfWork wuow(operationContext()); - catalog->setIndexIsMultikey(operationContext(), getCatalogId(), indexName, MultikeyPaths{}); + collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), MultikeyPaths{}); } DEATH_TEST_REGEX_F(DurableCatalogTest, AtLeastOnePathComponentMustCauseIndexToBeMultikey, R"#(Invariant failure.*somePathIsMultikey)#") { - std::string indexName = createIndex(BSON("a" << 1 << "b" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1 << "b" << 1)); + auto collection = getCollection(); WriteUnitOfWork wuow(operationContext()); - catalog->setIndexIsMultikey(operationContext(), - getCatalogId(), - indexName, - {MultikeyComponents{}, MultikeyComponents{}}); + collection->setIndexIsMultikey(operationContext(), + + indexEntry->descriptor()->indexName(), + {MultikeyComponents{}, MultikeyComponents{}}); } TEST_F(DurableCatalogTest, PathLevelMultikeyTrackingIsSupportedBy2dsphereIndexes) { std::string indexType = IndexNames::GEO_2DSPHERE; - std::string indexName = createIndex(BSON("a" << indexType << "b" << 1), indexType); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << indexType << "b" << 1), indexType); + auto collection = getCollection(); { MultikeyPaths multikeyPaths; - ASSERT(!catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(!collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); assertMultikeyPathsAreEqual(multikeyPaths, {MultikeyComponents{}, MultikeyComponents{}}); } } @@ -362,12 +400,12 @@ TEST_F(DurableCatalogTest, PathLevelMultikeyTrackingIsNotSupportedByAllIndexType IndexNames::GEO_2D, IndexNames::GEO_HAYSTACK, IndexNames::TEXT, IndexNames::HASHED}; for (auto&& indexType : indexTypes) { - std::string indexName = createIndex(BSON("a" << indexType << "b" << 1), indexType); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << indexType << "b" << 1), indexType); + auto collection = getCollection(); { MultikeyPaths multikeyPaths; - ASSERT(!catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(!collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); ASSERT(multikeyPaths.empty()); } } @@ -375,102 +413,107 @@ TEST_F(DurableCatalogTest, PathLevelMultikeyTrackingIsNotSupportedByAllIndexType TEST_F(DurableCatalogTest, CanSetEntireTextIndexAsMultikey) { std::string indexType = IndexNames::TEXT; - std::string indexName = createIndex(BSON("a" << indexType << "b" << 1), indexType); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << indexType << "b" << 1), indexType); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, MultikeyPaths{})); + ASSERT(collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), MultikeyPaths{})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); ASSERT(multikeyPaths.empty()); } } TEST_F(DurableCatalogTest, NoOpWhenEntireIndexAlreadySetAsMultikey) { std::string indexType = IndexNames::TEXT; - std::string indexName = createIndex(BSON("a" << indexType << "b" << 1), indexType); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << indexType << "b" << 1), indexType); + auto collection = getCollection(); { WriteUnitOfWork wuow(operationContext()); - ASSERT(catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, MultikeyPaths{})); + ASSERT(collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), MultikeyPaths{})); wuow.commit(); } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); ASSERT(multikeyPaths.empty()); } { WriteUnitOfWork wuow(operationContext()); - ASSERT(!catalog->setIndexIsMultikey( - operationContext(), getCatalogId(), indexName, MultikeyPaths{})); + ASSERT(!collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), MultikeyPaths{})); // Rollback WUOW. } { MultikeyPaths multikeyPaths; - ASSERT(catalog->isIndexMultikey( - operationContext(), getCatalogId(), indexName, &multikeyPaths)); + ASSERT(collection->isIndexMultikey( + operationContext(), indexEntry->descriptor()->indexName(), &multikeyPaths)); ASSERT(multikeyPaths.empty()); } } TEST_F(DurableCatalogTest, SinglePhaseIndexBuild) { - std::string indexName = createIndex(BSON("a" << 1)); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1)); + auto collection = getCollection(); - ASSERT_FALSE(catalog->isIndexReady(operationContext(), getCatalogId(), indexName)); - ASSERT_FALSE(catalog->getIndexBuildUUID(operationContext(), getCatalogId(), indexName)); + ASSERT_FALSE(collection->isIndexReady(indexEntry->descriptor()->indexName())); + ASSERT_FALSE(collection->getIndexBuildUUID(indexEntry->descriptor()->indexName())); { WriteUnitOfWork wuow(operationContext()); - catalog->indexBuildSuccess(operationContext(), getCatalogId(), indexName); + getCollectionWriter().getWritableCollection()->indexBuildSuccess(operationContext(), + indexEntry); wuow.commit(); } - ASSERT_TRUE(catalog->isIndexReady(operationContext(), getCatalogId(), indexName)); - ASSERT_FALSE(catalog->getIndexBuildUUID(operationContext(), getCatalogId(), indexName)); + collection = getCollection(); + ASSERT_TRUE(collection->isIndexReady(indexEntry->descriptor()->indexName())); + ASSERT_FALSE(collection->getIndexBuildUUID(indexEntry->descriptor()->indexName())); } TEST_F(DurableCatalogTest, TwoPhaseIndexBuild) { bool twoPhase = true; - std::string indexName = createIndex(BSON("a" << 1), IndexNames::BTREE, twoPhase); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << 1), IndexNames::BTREE, twoPhase); + auto collection = getCollection(); - ASSERT_FALSE(catalog->isIndexReady(operationContext(), getCatalogId(), indexName)); - ASSERT_TRUE(catalog->getIndexBuildUUID(operationContext(), getCatalogId(), indexName)); + ASSERT_FALSE(collection->isIndexReady(indexEntry->descriptor()->indexName())); + ASSERT_TRUE(collection->getIndexBuildUUID(indexEntry->descriptor()->indexName())); { WriteUnitOfWork wuow(operationContext()); - catalog->indexBuildSuccess(operationContext(), getCatalogId(), indexName); + getCollectionWriter().getWritableCollection()->indexBuildSuccess(operationContext(), + indexEntry); wuow.commit(); } - ASSERT_TRUE(catalog->isIndexReady(operationContext(), getCatalogId(), indexName)); - ASSERT_FALSE(catalog->getIndexBuildUUID(operationContext(), getCatalogId(), indexName)); + collection = getCollection(); + ASSERT_TRUE(collection->isIndexReady(indexEntry->descriptor()->indexName())); + ASSERT_FALSE(collection->getIndexBuildUUID(indexEntry->descriptor()->indexName())); } DEATH_TEST_REGEX_F(DurableCatalogTest, CannotSetIndividualPathComponentsOfTextIndexAsMultikey, R"#(Invariant failure.*multikeyPaths.empty\(\))#") { std::string indexType = IndexNames::TEXT; - std::string indexName = createIndex(BSON("a" << indexType << "b" << 1), indexType); - DurableCatalog* catalog = getCatalog(); + auto indexEntry = createIndex(BSON("a" << indexType << "b" << 1), indexType); + auto collection = getCollection(); WriteUnitOfWork wuow(operationContext()); - catalog->setIndexIsMultikey(operationContext(), getCatalogId(), indexName, {{0U}, {0U}}); + collection->setIndexIsMultikey( + operationContext(), indexEntry->descriptor()->indexName(), {{0U}, {0U}}); } TEST_F(DurableCatalogTest, ImportCollection) { @@ -546,7 +589,10 @@ TEST_F(DurableCatalogTest, IdentSuffixUsesRand) { const NamespaceString nss = NamespaceString("a.b"); - RecordId catalogId = createCollection(nss); + auto uuid = createCollection(nss); + auto collection = CollectionCatalog::get(operationContext()) + ->lookupCollectionByUUID(operationContext(), uuid); + RecordId catalogId = collection->getCatalogId(); ASSERT(StringData(getCatalog()->getEntry(catalogId).ident).endsWith(rand)); ASSERT_EQUALS(getCatalog()->getRand_forTest(), rand); } @@ -589,8 +635,9 @@ TEST_F(DurableCatalogTest, ImportCollectionRandConflict) { { // Check that a newly created collection doesn't use 'rand' as the suffix in the ident. const NamespaceString nss = NamespaceString("a.b"); + createCollection(nss); - RecordId catalogId = createCollection(nss); + RecordId catalogId = getCollection()->getCatalogId(); ASSERT(!StringData(getCatalog()->getEntry(catalogId).ident).endsWith(rand)); } diff --git a/src/mongo/db/storage/kv/storage_engine_test.cpp b/src/mongo/db/storage/kv/storage_engine_test.cpp index 74ac5ed28d5..6d64c063ee7 100644 --- a/src/mongo/db/storage/kv/storage_engine_test.cpp +++ b/src/mongo/db/storage/kv/storage_engine_test.cpp @@ -43,7 +43,6 @@ #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/service_context_d_test_fixture.h" #include "mongo/db/storage/devnull/devnull_kv_engine.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/storage/kv/kv_engine.h" #include "mongo/db/storage/storage_engine_impl.h" #include "mongo/db/storage/storage_engine_test_fixture.h" diff --git a/src/mongo/db/storage/storage_engine_impl.cpp b/src/mongo/db/storage/storage_engine_impl.cpp index 13f841b3c3b..77144ae5044 100644 --- a/src/mongo/db/storage/storage_engine_impl.cpp +++ b/src/mongo/db/storage/storage_engine_impl.cpp @@ -307,10 +307,10 @@ void StorageEngineImpl::_initCollection(OperationContext* opCtx, const NamespaceString& nss, bool forRepair, Timestamp minVisibleTs) { - BSONCollectionCatalogEntry::MetaData md = _catalog->getMetaData(opCtx, catalogId); + auto md = _catalog->getMetaData(opCtx, catalogId); uassert(ErrorCodes::MustDowngrade, str::stream() << "Collection does not have UUID in KVCatalog. Collection: " << nss, - md.options.uuid); + md->options.uuid); auto ident = _catalog->getEntry(catalogId).ident; @@ -320,18 +320,16 @@ void StorageEngineImpl::_initCollection(OperationContext* opCtx, // repaired. This also ensures that if we try to use it, it will blow up. rs = nullptr; } else { - rs = _engine->getRecordStore(opCtx, nss.ns(), ident, md.options); + rs = _engine->getRecordStore(opCtx, nss.ns(), ident, md->options); invariant(rs); } - auto options = _catalog->getCollectionOptions(opCtx, catalogId); - auto collectionFactory = Collection::Factory::get(getGlobalServiceContext()); - auto collection = collectionFactory->make(opCtx, nss, catalogId, options, std::move(rs)); + auto collection = collectionFactory->make(opCtx, nss, catalogId, md, std::move(rs)); collection->setMinimumVisibleSnapshot(minVisibleTs); CollectionCatalog::write(opCtx, [&](CollectionCatalog& catalog) { - catalog.registerCollection(opCtx, options.uuid.get(), std::move(collection)); + catalog.registerCollection(opCtx, md->options.uuid.get(), std::move(collection)); }); } @@ -367,7 +365,7 @@ Status StorageEngineImpl::_recoverOrphanedCollection(OperationContext* opCtx, WriteUnitOfWork wuow(opCtx); const auto metadata = _catalog->getMetaData(opCtx, catalogId); Status status = - _engine->recoverOrphanedIdent(opCtx, collectionName, collectionIdent, metadata.options); + _engine->recoverOrphanedIdent(opCtx, collectionName, collectionIdent, metadata->options); bool dataModified = status.code() == ErrorCodes::DataModifiedByRepair; if (!status.isOK() && !dataModified) { @@ -560,13 +558,13 @@ StatusWith<StorageEngine::ReconcileResult> StorageEngineImpl::reconcileCatalogAn // Also, remove unfinished builds except those that were background index builds started on a // secondary. for (DurableCatalog::Entry entry : catalogEntries) { - BSONCollectionCatalogEntry::MetaData metaData = + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> metaData = _catalog->getMetaData(opCtx, entry.catalogId); - NamespaceString coll(metaData.ns); + NamespaceString coll(metaData->ns); // Batch up the indexes to remove them from `metaData` outside of the iterator. std::vector<std::string> indexesToDrop; - for (const auto& indexMetaData : metaData.indexes) { + for (const auto& indexMetaData : metaData->indexes) { const std::string& indexName = indexMetaData.name(); std::string indexIdent = _catalog->getIndexIdent(opCtx, entry.catalogId, indexName); @@ -619,7 +617,7 @@ StatusWith<StorageEngine::ReconcileResult> StorageEngineImpl::reconcileCatalogAn if (indexMetaData.buildUUID) { invariant(!indexMetaData.ready); - auto collUUID = metaData.options.uuid; + auto collUUID = metaData->options.uuid; invariant(collUUID); auto buildUUID = *indexMetaData.buildUUID; @@ -676,13 +674,17 @@ StatusWith<StorageEngine::ReconcileResult> StorageEngineImpl::reconcileCatalogAn } for (auto&& indexName : indexesToDrop) { - invariant(metaData.eraseIndex(indexName), + invariant(metaData->eraseIndex(indexName), str::stream() << "Index is missing. Collection: " << coll << " Index: " << indexName); } if (indexesToDrop.size() > 0) { WriteUnitOfWork wuow(opCtx); - _catalog->putMetaData(opCtx, entry.catalogId, metaData); + auto collection = + CollectionCatalog::get(opCtx)->lookupCollectionByNamespaceForMetadataWrite( + opCtx, CollectionCatalog::LifetimeMode::kInplace, entry.nss); + invariant(collection->getCatalogId() == entry.catalogId); + collection->replaceMetadata(opCtx, std::move(metaData)); wuow.commit(); } } @@ -802,7 +804,8 @@ Status StorageEngineImpl::_dropCollectionsNoTimestamp(OperationContext* opCtx, WriteUnitOfWork untimestampedDropWuow(opCtx); auto collectionCatalog = CollectionCatalog::get(opCtx); for (auto& uuid : toDrop) { - auto coll = collectionCatalog->lookupCollectionByUUID(opCtx, uuid); + auto coll = collectionCatalog->lookupCollectionByUUIDForMetadataWrite( + opCtx, CollectionCatalog::LifetimeMode::kInplace, uuid); // No need to remove the indexes from the IndexCatalog because eliminating the Collection // will have the same effect. @@ -813,12 +816,8 @@ Status StorageEngineImpl::_dropCollectionsNoTimestamp(OperationContext* opCtx, audit::logDropIndex(opCtx->getClient(), ice->descriptor()->indexName(), coll->ns()); - catalog::removeIndex(opCtx, - ice->descriptor()->indexName(), - coll->getCatalogId(), - coll->uuid(), - coll->ns(), - ice->getSharedIdent()); + catalog::removeIndex( + opCtx, ice->descriptor()->indexName(), coll, ice->getSharedIdent()); } audit::logDropCollection(opCtx->getClient(), coll->ns()); @@ -1247,7 +1246,7 @@ int64_t StorageEngineImpl::sizeOnDiskForDb(OperationContext* opCtx, StringData d size += collection->getRecordStore()->storageSize(opCtx); std::vector<std::string> indexNames; - _catalog->getAllIndexes(opCtx, collection->getCatalogId(), &indexNames); + collection->getAllIndexes(&indexNames); for (size_t i = 0; i < indexNames.size(); i++) { std::string ident = diff --git a/src/mongo/db/storage/storage_engine_test_fixture.h b/src/mongo/db/storage/storage_engine_test_fixture.h index 9ef8d7252c1..57b50a1d32c 100644 --- a/src/mongo/db/storage/storage_engine_test_fixture.h +++ b/src/mongo/db/storage/storage_engine_test_fixture.h @@ -32,7 +32,7 @@ #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault #include "mongo/db/catalog/collection_catalog.h" -#include "mongo/db/catalog/collection_mock.h" +#include "mongo/db/catalog/collection_impl.h" #include "mongo/db/catalog_raii.h" #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/service_context_d_test_fixture.h" @@ -69,7 +69,12 @@ public: _storageEngine->getCatalog()->createCollection(opCtx, ns, options, true)); wuow.commit(); } - std::shared_ptr<Collection> coll = std::make_shared<CollectionMock>(ns, catalogId); + std::shared_ptr<Collection> coll = std::make_shared<CollectionImpl>( + opCtx, + ns, + catalogId, + _storageEngine->getCatalog()->getMetaData(opCtx, catalogId), + std::move(rs)); CollectionCatalog::write(opCtx, [&](CollectionCatalog& catalog) { catalog.registerCollection(opCtx, options.uuid.get(), std::move(coll)); }); @@ -159,22 +164,22 @@ public: } BSONObj spec = builder.append("name", key).append("v", 2).done(); - CollectionPtr collection = - CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, collNs); + Collection* collection = + CollectionCatalog::get(opCtx)->lookupCollectionByNamespaceForMetadataWrite( + opCtx, CollectionCatalog::LifetimeMode::kInplace, collNs); auto descriptor = std::make_unique<IndexDescriptor>(IndexNames::findPluginName(spec), spec); - auto ret = DurableCatalog::get(opCtx)->prepareForIndexBuild(opCtx, - collection->getCatalogId(), - descriptor.get(), - buildUUID, - isBackgroundSecondaryBuild); + auto ret = collection->prepareForIndexBuild( + opCtx, descriptor.get(), buildUUID, isBackgroundSecondaryBuild); return ret; } void indexBuildSuccess(OperationContext* opCtx, NamespaceString collNs, std::string key) { - CollectionPtr collection = - CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, collNs); - DurableCatalog::get(opCtx)->indexBuildSuccess(opCtx, collection->getCatalogId(), key); + Collection* collection = + CollectionCatalog::get(opCtx)->lookupCollectionByNamespaceForMetadataWrite( + opCtx, CollectionCatalog::LifetimeMode::kInplace, collNs); + auto descriptor = collection->getIndexCatalog()->findIndexByName(opCtx, key, true); + collection->indexBuildSuccess(opCtx, descriptor->getEntry()); } Status removeEntry(OperationContext* opCtx, StringData collNs, DurableCatalog* catalog) { diff --git a/src/mongo/db/storage/storage_util.cpp b/src/mongo/db/storage/storage_util.cpp index f4d5b8ae8ee..81380e9eae2 100644 --- a/src/mongo/db/storage/storage_util.cpp +++ b/src/mongo/db/storage/storage_util.cpp @@ -83,9 +83,7 @@ auto removeEmptyDirectory = void removeIndex(OperationContext* opCtx, StringData indexName, - RecordId collectionCatalogId, - UUID collectionUUID, - const NamespaceString& nss, + Collection* collection, std::shared_ptr<Ident> ident) { auto durableCatalog = DurableCatalog::get(opCtx); @@ -94,11 +92,11 @@ void removeIndex(OperationContext* opCtx, // to wait for existing users to finish. if (!ident) { ident = std::make_shared<Ident>( - durableCatalog->getIndexIdent(opCtx, collectionCatalogId, indexName)); + durableCatalog->getIndexIdent(opCtx, collection->getCatalogId(), indexName)); } // Run the first phase of drop to remove the catalog entry. - durableCatalog->removeIndex(opCtx, collectionCatalogId, indexName); + collection->removeIndex(opCtx, indexName); // The OperationContext may not be valid when the RecoveryUnit executes the onCommit handlers. // Therefore, anything that would normally be fetched from the opCtx must be passed in @@ -114,8 +112,8 @@ void removeIndex(OperationContext* opCtx, opCtx->recoveryUnit()->onCommit([svcCtx = opCtx->getServiceContext(), recoveryUnit, storageEngine, - collectionUUID, - nss, + uuid = collection->uuid(), + nss = collection->ns(), indexNameStr = indexName.toString(), ident](boost::optional<Timestamp> commitTimestamp) { StorageEngine::DropIdentCallback onDrop = [svcCtx, storageEngine, nss] { @@ -131,7 +129,7 @@ void removeIndex(OperationContext* opCtx, "Deferring table drop for index", "index"_attr = indexNameStr, logAttrs(nss), - "uuid"_attr = collectionUUID, + "uuid"_attr = uuid, "ident"_attr = ident->getIdent(), "commitTimestamp"_attr = commitTimestamp); storageEngine->addDropPendingIdent(*commitTimestamp, ident, std::move(onDrop)); diff --git a/src/mongo/db/storage/storage_util.h b/src/mongo/db/storage/storage_util.h index 69351f63c2b..ba5e9aedbef 100644 --- a/src/mongo/db/storage/storage_util.h +++ b/src/mongo/db/storage/storage_util.h @@ -34,6 +34,7 @@ namespace mongo { +class Collection; class Ident; class OperationContext; class NamespaceString; @@ -54,9 +55,7 @@ namespace catalog { */ void removeIndex(OperationContext* opCtx, StringData indexName, - RecordId collectionCatalogId, - UUID collectionUUID, - const NamespaceString& nss, + Collection* collection, std::shared_ptr<Ident> ident); /** diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp index f851a48e0a5..bebf1c02c7c 100644 --- a/src/mongo/db/transaction_participant.cpp +++ b/src/mongo/db/transaction_participant.cpp @@ -761,6 +761,7 @@ TransactionParticipant::TxnResources::TxnResources(WithLock wl, _apiParameters = APIParameters::get(opCtx); _readConcernArgs = repl::ReadConcernArgs::get(opCtx); _uncommittedCollections = UncommittedCollections::get(opCtx).releaseResources(); + _uncommittedMultikey = UncommittedMultikey::get(opCtx).releaseResources(); } TransactionParticipant::TxnResources::~TxnResources() { @@ -828,6 +829,10 @@ void TransactionParticipant::TxnResources::release(OperationContext* opCtx) { UncommittedCollections::get(opCtx).receiveResources(_uncommittedCollections); _uncommittedCollections = nullptr; + // Transfer ownership of UncommittedMultikey + UncommittedMultikey::get(opCtx).receiveResources(_uncommittedMultikey); + _uncommittedMultikey = nullptr; + auto oldState = opCtx->setRecoveryUnit(std::move(_recoveryUnit), WriteUnitOfWork::RecoveryUnitState::kNotInUnitOfWork); invariant(oldState == WriteUnitOfWork::RecoveryUnitState::kNotInUnitOfWork, @@ -1158,7 +1163,7 @@ Timestamp TransactionParticipant::Participant::prepareTransaction( str::stream() << "prepareTransaction failed because one of the transaction " "operations was done against a temporary collection '" << collection->ns() << "'.", - !collection->isTemporary(opCtx)); + !collection->isTemporary()); } boost::optional<OplogSlotReserver> oplogSlotReserver; diff --git a/src/mongo/db/transaction_participant.h b/src/mongo/db/transaction_participant.h index 4427ed7d85a..de02c0e3e9c 100644 --- a/src/mongo/db/transaction_participant.h +++ b/src/mongo/db/transaction_participant.h @@ -35,6 +35,7 @@ #include "mongo/db/api_parameters.h" #include "mongo/db/catalog/uncommitted_collections.h" +#include "mongo/db/catalog/uncommitted_multikey.h" #include "mongo/db/commands/txn_cmds_gen.h" #include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/concurrency/locker.h" @@ -232,6 +233,7 @@ public: repl::ReadConcernArgs _readConcernArgs; WriteUnitOfWork::RecoveryUnitState _ruState; std::shared_ptr<UncommittedCollections::UncommittedCollectionsMap> _uncommittedCollections; + std::shared_ptr<UncommittedMultikey::MultikeyMap> _uncommittedMultikey; }; /** diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp index 05d633f9e47..bf7bca72dc1 100644 --- a/src/mongo/db/ttl.cpp +++ b/src/mongo/db/ttl.cpp @@ -54,7 +54,6 @@ #include "mongo/db/repl/tenant_migration_access_blocker_registry.h" #include "mongo/db/service_context.h" #include "mongo/db/stats/resource_consumption_metrics.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/db/timeseries/bucket_catalog.h" #include "mongo/db/ttl_collection_cache.h" #include "mongo/db/ttl_gen.h" @@ -326,21 +325,18 @@ private: TTLCollectionCache* ttlCollectionCache, const CollectionPtr& collection, std::string indexName) { - if (!DurableCatalog::get(opCtx)->isIndexPresent( - opCtx, collection->getCatalogId(), indexName)) { + if (!collection->isIndexPresent(indexName)) { ttlCollectionCache->deregisterTTLInfo(collection->uuid(), indexName); return; } - BSONObj spec = - DurableCatalog::get(opCtx)->getIndexSpec(opCtx, collection->getCatalogId(), indexName); + BSONObj spec = collection->getIndexSpec(indexName); if (!spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) { ttlCollectionCache->deregisterTTLInfo(collection->uuid(), indexName); return; } - if (!DurableCatalog::get(opCtx)->isIndexReady( - opCtx, collection->getCatalogId(), indexName)) { + if (!collection->isIndexReady(indexName)) { return; } @@ -451,8 +447,7 @@ private: void deleteExpiredWithCollscan(OperationContext* opCtx, TTLCollectionCache* ttlCollectionCache, const CollectionPtr& collection) { - auto collOptions = - DurableCatalog::get(opCtx)->getCollectionOptions(opCtx, collection->getCatalogId()); + const auto& collOptions = collection->getCollectionOptions(); uassert(5400701, "collection is not clustered by _id but is described as being TTL", collOptions.clusteredIndex); diff --git a/src/mongo/dbtests/catalogtests.cpp b/src/mongo/dbtests/catalogtests.cpp index e980a010e3f..5df1d340b30 100644 --- a/src/mongo/dbtests/catalogtests.cpp +++ b/src/mongo/dbtests/catalogtests.cpp @@ -38,7 +38,6 @@ #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/dbtests/dbtests.h" #include "mongo/unittest/unittest.h" diff --git a/src/mongo/dbtests/counttests.cpp b/src/mongo/dbtests/counttests.cpp index 4f89a1cce3b..45318587cc2 100644 --- a/src/mongo/dbtests/counttests.cpp +++ b/src/mongo/dbtests/counttests.cpp @@ -63,7 +63,8 @@ public: auto indexSpec = BSON("v" << static_cast<int>(IndexDescriptor::kLatestIndexVersion) << "key" << BSON("a" << 1) << "name" << "a_1"); - uassertStatusOK(indexCatalog->createIndexOnEmptyCollection(&_opCtx, indexSpec)); + uassertStatusOK( + indexCatalog->createIndexOnEmptyCollection(&_opCtx, collection, indexSpec)); wunit.commit(); diff --git a/src/mongo/dbtests/indexcatalogtests.cpp b/src/mongo/dbtests/indexcatalogtests.cpp index f7427a56063..0afe4634f4f 100644 --- a/src/mongo/dbtests/indexcatalogtests.cpp +++ b/src/mongo/dbtests/indexcatalogtests.cpp @@ -34,7 +34,6 @@ #include "mongo/db/client.h" #include "mongo/db/db_raii.h" #include "mongo/db/index/index_descriptor.h" -#include "mongo/db/storage/durable_catalog.h" #include "mongo/dbtests/dbtests.h" namespace IndexCatalogTests { @@ -202,22 +201,22 @@ public: ASSERT(desc); ASSERT_EQUALS(5, desc->infoObj()["expireAfterSeconds"].numberLong()); - // Change value of "expireAfterSeconds" on disk. + // Change value of "expireAfterSeconds" on disk. This will update the metadata for the + // Collection but not propagate the change to the IndexCatalog { WriteUnitOfWork wuow(&opCtx); - opCtx.getServiceContext()->getStorageEngine()->getCatalog()->updateTTLSetting( - &opCtx, _coll->getCatalogId(), "x_1", 10); + _coll->updateTTLSetting(&opCtx, "x_1", 10); wuow.commit(); } - // Verify that the catalog does not yet know of the change. + // Confirm that the index catalog does not yet know of the change. desc = _catalog->findIndexByName(&opCtx, indexName); ASSERT_EQUALS(5, desc->infoObj()["expireAfterSeconds"].numberLong()); { // Notify the catalog of the change. WriteUnitOfWork wuow(&opCtx); - desc = _catalog->refreshEntry(&opCtx, desc); + desc = _catalog->refreshEntry(&opCtx, _coll, desc); wuow.commit(); } diff --git a/src/mongo/dbtests/indexupdatetests.cpp b/src/mongo/dbtests/indexupdatetests.cpp index c01f5439859..497e630135f 100644 --- a/src/mongo/dbtests/indexupdatetests.cpp +++ b/src/mongo/dbtests/indexupdatetests.cpp @@ -249,7 +249,8 @@ public: { WriteUnitOfWork wunit(_opCtx); // Drop all indexes including id index. - coll.getWritableCollection()->getIndexCatalog()->dropAllIndexes(_opCtx, true); + coll.getWritableCollection()->getIndexCatalog()->dropAllIndexes( + _opCtx, coll.getWritableCollection(), true); // Insert some documents. int32_t nDocs = 1000; OpDebug* const nullOpDebug = nullptr; @@ -299,7 +300,7 @@ public: options.capped = true; options.cappedSize = 10 * 1024; Collection* coll = db->createCollection(_opCtx, _nss, options); - coll->getIndexCatalog()->dropAllIndexes(_opCtx, true); + coll->getIndexCatalog()->dropAllIndexes(_opCtx, coll, true); // Insert some documents. int32_t nDocs = 1000; OpDebug* const nullOpDebug = nullptr; diff --git a/src/mongo/dbtests/query_stage_count.cpp b/src/mongo/dbtests/query_stage_count.cpp index 1456bb2c231..e6e32ddec35 100644 --- a/src/mongo/dbtests/query_stage_count.cpp +++ b/src/mongo/dbtests/query_stage_count.cpp @@ -75,6 +75,7 @@ public: coll->getIndexCatalog() ->createIndexOnEmptyCollection(&_opCtx, + coll, BSON("key" << BSON("x" << 1) << "name" << "x_1" << "v" << 1)) diff --git a/src/mongo/dbtests/query_stage_ixscan.cpp b/src/mongo/dbtests/query_stage_ixscan.cpp index 4aa7462de72..b574b51ec84 100644 --- a/src/mongo/dbtests/query_stage_ixscan.cpp +++ b/src/mongo/dbtests/query_stage_ixscan.cpp @@ -63,6 +63,7 @@ public: ASSERT_OK(_coll->getIndexCatalog()->createIndexOnEmptyCollection( &_opCtx, + _coll, BSON("key" << BSON("x" << 1) << "name" << DBClientBase::genIndexName(BSON("x" << 1)) << "v" << static_cast<int>(kIndexVersion)))); diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp index 3144d699bca..95310d74bc1 100644 --- a/src/mongo/dbtests/rollbacktests.cpp +++ b/src/mongo/dbtests/rollbacktests.cpp @@ -146,7 +146,8 @@ void dropIndex(OperationContext* opCtx, const NamespaceString& nss, const string CollectionWriter coll(opCtx, nss); auto desc = coll.getWritableCollection()->getIndexCatalog()->findIndexByName(opCtx, idxName); ASSERT(desc); - ASSERT_OK(coll.getWritableCollection()->getIndexCatalog()->dropIndex(opCtx, desc)); + ASSERT_OK(coll.getWritableCollection()->getIndexCatalog()->dropIndex( + opCtx, coll.getWritableCollection(), desc)); } } // namespace @@ -497,7 +498,8 @@ public: { WriteUnitOfWork uow(&opCtx); IndexCatalog* catalog = coll.getWritableCollection()->getIndexCatalog(); - ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, spec)); + ASSERT_OK( + catalog->createIndexOnEmptyCollection(&opCtx, coll.getWritableCollection(), spec)); insertRecord(&opCtx, nss, BSON("a" << 1)); insertRecord(&opCtx, nss, BSON("a" << 2)); insertRecord(&opCtx, nss, BSON("a" << 3)); @@ -536,7 +538,8 @@ public: { WriteUnitOfWork uow(&opCtx); IndexCatalog* catalog = coll.getWritableCollection()->getIndexCatalog(); - ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, spec)); + ASSERT_OK( + catalog->createIndexOnEmptyCollection(&opCtx, coll.getWritableCollection(), spec)); insertRecord(&opCtx, nss, BSON("a" << 1)); insertRecord(&opCtx, nss, BSON("a" << 2)); insertRecord(&opCtx, nss, BSON("a" << 3)); @@ -591,7 +594,8 @@ public: WriteUnitOfWork uow(&opCtx); IndexCatalog* catalog = coll.getWritableCollection()->getIndexCatalog(); - ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, spec)); + ASSERT_OK( + catalog->createIndexOnEmptyCollection(&opCtx, coll.getWritableCollection(), spec)); insertRecord(&opCtx, nss, BSON("a" << 1)); insertRecord(&opCtx, nss, BSON("a" << 2)); insertRecord(&opCtx, nss, BSON("a" << 3)); @@ -643,11 +647,12 @@ public: ASSERT_OK(ctx.db()->userCreateNS(&opCtx, nss, collectionOptions, false)); ASSERT(collectionExists(&opCtx, &ctx, nss.ns())); CollectionWriter coll(&opCtx, nss); - IndexCatalog* catalog = coll.getWritableCollection()->getIndexCatalog(); + auto writableColl = coll.getWritableCollection(); + IndexCatalog* catalog = writableColl->getIndexCatalog(); - ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, specA)); - ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, specB)); - ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, specC)); + ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, writableColl, specA)); + ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, writableColl, specB)); + ASSERT_OK(catalog->createIndexOnEmptyCollection(&opCtx, writableColl, specC)); if (!rollback) { uow.commit(); diff --git a/src/mongo/dbtests/storage_timestamp_tests.cpp b/src/mongo/dbtests/storage_timestamp_tests.cpp index 74fbbea51bc..dd25e73d3b0 100644 --- a/src/mongo/dbtests/storage_timestamp_tests.cpp +++ b/src/mongo/dbtests/storage_timestamp_tests.cpp @@ -140,17 +140,17 @@ private: const auto kIndexVersion = IndexDescriptor::IndexVersion::kV2; -void assertIndexMetaDataMissing(const BSONCollectionCatalogEntry::MetaData& collMetaData, +void assertIndexMetaDataMissing(std::shared_ptr<BSONCollectionCatalogEntry::MetaData> collMetaData, StringData indexName) { - const auto idxOffset = collMetaData.findIndexOffset(indexName); - ASSERT_EQUALS(-1, idxOffset) << indexName << ". Collection Metdata: " << collMetaData.toBSON(); + const auto idxOffset = collMetaData->findIndexOffset(indexName); + ASSERT_EQUALS(-1, idxOffset) << indexName << ". Collection Metdata: " << collMetaData->toBSON(); } BSONCollectionCatalogEntry::IndexMetaData getIndexMetaData( - const BSONCollectionCatalogEntry::MetaData& collMetaData, StringData indexName) { - const auto idxOffset = collMetaData.findIndexOffset(indexName); + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> collMetaData, StringData indexName) { + const auto idxOffset = collMetaData->findIndexOffset(indexName); ASSERT_GT(idxOffset, -1) << indexName; - return collMetaData.indexes[idxOffset]; + return collMetaData->indexes[idxOffset]; } class DoNothingOplogApplierObserver : public repl::OplogApplier::Observer { @@ -245,7 +245,8 @@ public: if (_opCtx->recoveryUnit()->getCommitTimestamp().isNull()) { ASSERT_OK(_opCtx->recoveryUnit()->setTimestamp(Timestamp(1, 1))); } - collRaii.getWritableCollection()->getIndexCatalog()->dropAllIndexes(_opCtx, false); + collRaii.getWritableCollection()->getIndexCatalog()->dropAllIndexes( + _opCtx, collRaii.getWritableCollection(), false); wunit.commit(); return; } @@ -327,9 +328,8 @@ public: return optRecord.get().data.toBson(); } - BSONCollectionCatalogEntry::MetaData getMetaDataAtTime(DurableCatalog* durableCatalog, - RecordId catalogId, - const Timestamp& ts) { + std::shared_ptr<BSONCollectionCatalogEntry::MetaData> getMetaDataAtTime( + DurableCatalog* durableCatalog, RecordId catalogId, const Timestamp& ts) { OneOffRead oor(_opCtx, ts); return durableCatalog->getMetaData(_opCtx, catalogId); } @@ -3308,12 +3308,12 @@ public: auto systemViewsMd = getMetaDataAtTime( durableCatalog, catalogId, Timestamp(systemViewsCreateTs.asULL() - 1)); - ASSERT_EQ("", systemViewsMd.ns) + ASSERT(systemViewsMd == nullptr) << systemViewsNss << " incorrectly exists before creation. CreateTs: " << systemViewsCreateTs; systemViewsMd = getMetaDataAtTime(durableCatalog, catalogId, systemViewsCreateTs); - ASSERT_EQ(systemViewsNss.ns(), systemViewsMd.ns); + ASSERT_EQ(systemViewsNss.ns(), systemViewsMd->ns); assertDocumentAtTimestamp(autoColl.getCollection(), systemViewsCreateTs, BSONObj()); assertDocumentAtTimestamp(autoColl.getCollection(), diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index 49d8b8aa226..a1db385e352 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -3331,6 +3331,7 @@ public: coll = _db->createCollection(&_opCtx, _nss); wunit.commit(); } + auto writableCollection = const_cast<Collection*>(coll.get()); const auto indexName = "mk_index"; auto status = dbtests::createIndexFromSpec(&_opCtx, @@ -3346,12 +3347,13 @@ public: WriteUnitOfWork wunit(&_opCtx); auto collMetadata = DurableCatalog::get(&_opCtx)->getMetaData(&_opCtx, coll->getCatalogId()); - int offset = collMetadata.findIndexOffset(indexName); + int offset = collMetadata->findIndexOffset(indexName); ASSERT_GTE(offset, 0); - auto& indexMetadata = collMetadata.indexes[offset]; + auto& indexMetadata = collMetadata->indexes[offset]; indexMetadata.multikeyPaths = {}; - DurableCatalog::get(&_opCtx)->putMetaData(&_opCtx, coll->getCatalogId(), collMetadata); + auto writableCollection = const_cast<Collection*>(coll.get()); + writableCollection->replaceMetadata(&_opCtx, std::move(collMetadata)); wunit.commit(); } @@ -3361,7 +3363,7 @@ public: { WriteUnitOfWork wunit(&_opCtx); auto writableCatalog = const_cast<IndexCatalog*>(indexCatalog); - descriptor = writableCatalog->refreshEntry(&_opCtx, descriptor); + descriptor = writableCatalog->refreshEntry(&_opCtx, writableCollection, descriptor); wunit.commit(); } |