diff options
Diffstat (limited to 'src/mongo/dbtests/indexupdatetests.cpp')
-rw-r--r-- | src/mongo/dbtests/indexupdatetests.cpp | 265 |
1 files changed, 144 insertions, 121 deletions
diff --git a/src/mongo/dbtests/indexupdatetests.cpp b/src/mongo/dbtests/indexupdatetests.cpp index 19b1a22a8c5..21e45f0adbd 100644 --- a/src/mongo/dbtests/indexupdatetests.cpp +++ b/src/mongo/dbtests/indexupdatetests.cpp @@ -34,6 +34,8 @@ #include "mongo/db/catalog/collection.h" #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/catalog/multi_index_block.h" +#include "mongo/db/catalog/uncommitted_collections.h" +#include "mongo/db/catalog_raii.h" #include "mongo/db/client.h" #include "mongo/db/db_raii.h" #include "mongo/db/dbdirectclient.h" @@ -58,33 +60,46 @@ static const NamespaceString _nss = NamespaceString(_ns); */ class IndexBuildBase { public: - IndexBuildBase() : _ctx(&_opCtx, _ns), _client(&_opCtx) { - _client.createCollection(_ns); + IndexBuildBase() { + regenOpCtx(); + + AutoGetOrCreateDb db(_opCtx, _nss.db(), LockMode::MODE_IX); + WriteUnitOfWork wuow(_opCtx); + Lock::CollectionLock lk(_opCtx, _nss, LockMode::MODE_IX); + db.getDb()->createCollection(_opCtx, _nss); + wuow.commit(); } + ~IndexBuildBase() { - _client.dropCollection(_ns); getGlobalServiceContext()->unsetKillAllOperations(); + + AutoGetOrCreateDb db(_opCtx, _nss.db(), LockMode::MODE_IX); + WriteUnitOfWork wuow(_opCtx); + Lock::CollectionLock lk(_opCtx, _nss, LockMode::MODE_X); + ASSERT_OK(db.getDb()->dropCollection(_opCtx, _nss, {})); + wuow.commit(); } + Collection* collection() { - return _ctx.getCollection(); + return CollectionCatalog::get(_opCtx).lookupCollectionByNamespace(_opCtx, _nss); } protected: - Status createIndex(const std::string& dbname, const BSONObj& indexSpec); + Status createIndex(const BSONObj& indexSpec); bool buildIndexInterrupted(const BSONObj& key) { try { MultiIndexBlock indexer; ON_BLOCK_EXIT([&] { - indexer.cleanUpAfterBuild(&_opCtx, collection(), MultiIndexBlock::kNoopOnCleanUpFn); + indexer.cleanUpAfterBuild(_opCtx, collection(), MultiIndexBlock::kNoopOnCleanUpFn); }); uassertStatusOK( - indexer.init(&_opCtx, collection(), key, MultiIndexBlock::kNoopOnInitFn)); - uassertStatusOK(indexer.insertAllDocumentsInCollection(&_opCtx, collection())); - WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(indexer.commit(&_opCtx, + indexer.init(_opCtx, collection(), key, MultiIndexBlock::kNoopOnInitFn)); + uassertStatusOK(indexer.insertAllDocumentsInCollection(_opCtx, collection())); + WriteUnitOfWork wunit(_opCtx); + ASSERT_OK(indexer.commit(_opCtx, collection(), MultiIndexBlock::kNoopOnCreateEachFn, MultiIndexBlock::kNoopOnCommitFn)); @@ -98,10 +113,14 @@ protected: return false; } - const ServiceContext::UniqueOperationContext _txnPtr = cc().makeOperationContext(); - OperationContext& _opCtx = *_txnPtr; - dbtests::WriteContextForTests _ctx; - DBDirectClient _client; + void regenOpCtx() { + _txnPtr = nullptr; + _txnPtr = cc().makeOperationContext(); + _opCtx = _txnPtr.get(); + } + + ServiceContext::UniqueOperationContext _txnPtr; // = cc().makeOperationContext(); + OperationContext* _opCtx; // = _txnPtr.get(); }; /** Index creation ignores unique constraints when told to. */ @@ -109,21 +128,18 @@ template <bool background> class InsertBuildIgnoreUnique : public IndexBuildBase { public: void run() { - // Create a new collection. - Database* db = _ctx.db(); - Collection* coll; + AutoGetOrCreateDb dbRaii(_opCtx, _nss.db(), LockMode::MODE_IX); + Lock::CollectionLock collLk(_opCtx, _nss, LockMode::MODE_X); + Collection* coll = collection(); { - WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _nss)); - coll = db->createCollection(&_opCtx, _nss); - + WriteUnitOfWork wunit(_opCtx); OpDebug* const nullOpDebug = nullptr; - ASSERT_OK(coll->insertDocument(&_opCtx, + ASSERT_OK(coll->insertDocument(_opCtx, InsertStatement(BSON("_id" << 1 << "a" << "dup")), nullOpDebug, true)); - ASSERT_OK(coll->insertDocument(&_opCtx, + ASSERT_OK(coll->insertDocument(_opCtx, InsertStatement(BSON("_id" << 2 << "a" << "dup")), nullOpDebug, @@ -141,15 +157,15 @@ public: << "background" << background); ON_BLOCK_EXIT( - [&] { indexer.cleanUpAfterBuild(&_opCtx, coll, MultiIndexBlock::kNoopOnCleanUpFn); }); + [&] { indexer.cleanUpAfterBuild(_opCtx, coll, MultiIndexBlock::kNoopOnCleanUpFn); }); - ASSERT_OK(indexer.init(&_opCtx, coll, spec, MultiIndexBlock::kNoopOnInitFn).getStatus()); - ASSERT_OK(indexer.insertAllDocumentsInCollection(&_opCtx, coll)); - ASSERT_OK(indexer.checkConstraints(&_opCtx)); + ASSERT_OK(indexer.init(_opCtx, coll, spec, MultiIndexBlock::kNoopOnInitFn).getStatus()); + ASSERT_OK(indexer.insertAllDocumentsInCollection(_opCtx, coll)); + ASSERT_OK(indexer.checkConstraints(_opCtx)); - WriteUnitOfWork wunit(&_opCtx); + WriteUnitOfWork wunit(_opCtx); ASSERT_OK(indexer.commit( - &_opCtx, coll, MultiIndexBlock::kNoopOnCreateEachFn, MultiIndexBlock::kNoopOnCommitFn)); + _opCtx, coll, MultiIndexBlock::kNoopOnCreateEachFn, MultiIndexBlock::kNoopOnCommitFn)); wunit.commit(); } }; @@ -160,20 +176,19 @@ class InsertBuildEnforceUnique : public IndexBuildBase { public: void run() { // Create a new collection. - Database* db = _ctx.db(); - Collection* coll; + AutoGetOrCreateDb dbRaii(_opCtx, _nss.db(), LockMode::MODE_IX); + boost::optional<Lock::CollectionLock> collLk; + collLk.emplace(_opCtx, _nss, LockMode::MODE_IX); + Collection* coll = collection(); { - WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _nss)); - coll = db->createCollection(&_opCtx, _nss); - + WriteUnitOfWork wunit(_opCtx); OpDebug* const nullOpDebug = nullptr; - ASSERT_OK(coll->insertDocument(&_opCtx, + ASSERT_OK(coll->insertDocument(_opCtx, InsertStatement(BSON("_id" << 1 << "a" << "dup")), nullOpDebug, true)); - ASSERT_OK(coll->insertDocument(&_opCtx, + ASSERT_OK(coll->insertDocument(_opCtx, InsertStatement(BSON("_id" << 2 << "a" << "dup")), nullOpDebug, @@ -189,19 +204,20 @@ public: << static_cast<int>(kIndexVersion) << "unique" << true << "background" << background); + collLk.emplace(_opCtx, _nss, LockMode::MODE_X); ON_BLOCK_EXIT( - [&] { indexer.cleanUpAfterBuild(&_opCtx, coll, MultiIndexBlock::kNoopOnCleanUpFn); }); + [&] { indexer.cleanUpAfterBuild(_opCtx, coll, MultiIndexBlock::kNoopOnCleanUpFn); }); - ASSERT_OK(indexer.init(&_opCtx, coll, spec, MultiIndexBlock::kNoopOnInitFn).getStatus()); + ASSERT_OK(indexer.init(_opCtx, coll, spec, MultiIndexBlock::kNoopOnInitFn).getStatus()); auto desc = - coll->getIndexCatalog()->findIndexByName(&_opCtx, "a", true /* includeUnfinished */); + coll->getIndexCatalog()->findIndexByName(_opCtx, "a", true /* includeUnfinished */); ASSERT(desc); // Hybrid index builds check duplicates explicitly. - ASSERT_OK(indexer.insertAllDocumentsInCollection(&_opCtx, coll)); + ASSERT_OK(indexer.insertAllDocumentsInCollection(_opCtx, coll)); - auto status = indexer.checkConstraints(&_opCtx); + auto status = indexer.checkConstraints(_opCtx); ASSERT_EQUALS(status.code(), ErrorCodes::DuplicateKey); } }; @@ -210,35 +226,42 @@ public: class InsertBuildIndexInterrupt : public IndexBuildBase { public: void run() { - // Create a new collection. - Database* db = _ctx.db(); - Collection* coll; { - WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _nss)); - coll = db->createCollection(&_opCtx, _nss); - // Drop all indexes including id index. - coll->getIndexCatalog()->dropAllIndexes(&_opCtx, true); - // Insert some documents. - int32_t nDocs = 1000; - OpDebug* const nullOpDebug = nullptr; - for (int32_t i = 0; i < nDocs; ++i) { - ASSERT_OK( - coll->insertDocument(&_opCtx, InsertStatement(BSON("a" << i)), nullOpDebug)); + AutoGetOrCreateDb dbRaii(_opCtx, _nss.db(), LockMode::MODE_IX); + boost::optional<Lock::CollectionLock> collLk; + collLk.emplace(_opCtx, _nss, LockMode::MODE_X); + + Collection* coll = collection(); + { + WriteUnitOfWork wunit(_opCtx); + // Drop all indexes including id index. + coll->getIndexCatalog()->dropAllIndexes(_opCtx, true); + // Insert some documents. + int32_t nDocs = 1000; + OpDebug* const nullOpDebug = nullptr; + for (int32_t i = 0; i < nDocs; ++i) { + ASSERT_OK( + coll->insertDocument(_opCtx, InsertStatement(BSON("a" << i)), nullOpDebug)); + } + wunit.commit(); } - wunit.commit(); + // Request an interrupt. + getGlobalServiceContext()->setKillAllOperations(); + BSONObj indexInfo = BSON("key" << BSON("a" << 1) << "name" + << "a_1" + << "v" << static_cast<int>(kIndexVersion)); + // The call is interrupted because mayInterrupt == true. + ASSERT_TRUE(buildIndexInterrupted(indexInfo)); + // only want to interrupt the index build + getGlobalServiceContext()->unsetKillAllOperations(); } - // Request an interrupt. - getGlobalServiceContext()->setKillAllOperations(); - BSONObj indexInfo = BSON("key" << BSON("a" << 1) << "name" - << "a_1" - << "v" << static_cast<int>(kIndexVersion)); - // The call is interrupted because mayInterrupt == true. - ASSERT_TRUE(buildIndexInterrupted(indexInfo)); - // only want to interrupt the index build - getGlobalServiceContext()->unsetKillAllOperations(); + + regenOpCtx(); + AutoGetDb dbRaii(_opCtx, _nss.db(), LockMode::MODE_IX); + boost::optional<Lock::CollectionLock> collLk; + collLk.emplace(_opCtx, _nss, LockMode::MODE_IX); // The new index is not listed in the index catalog because the index build failed. - ASSERT(!coll->getIndexCatalog()->findIndexByName(&_opCtx, "a_1")); + ASSERT(!collection()->getIndexCatalog()->findIndexByName(_opCtx, "a_1")); } }; @@ -251,62 +274,73 @@ public: return; } - // Recreate the collection as capped, without an _id index. - Database* db = _ctx.db(); - Collection* coll; { - WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _nss)); + // Recreate the collection as capped, without an _id index. + AutoGetOrCreateDb dbRaii(_opCtx, _nss.db(), LockMode::MODE_IX); + Database* db = dbRaii.getDb(); + boost::optional<Lock::CollectionLock> collLk; + collLk.emplace(_opCtx, _nss, LockMode::MODE_X); + + WriteUnitOfWork wunit(_opCtx); + ASSERT_OK(db->dropCollection(_opCtx, _nss)); CollectionOptions options; options.capped = true; options.cappedSize = 10 * 1024; - coll = db->createCollection(&_opCtx, _nss, options); - coll->getIndexCatalog()->dropAllIndexes(&_opCtx, true); + Collection* coll = db->createCollection(_opCtx, _nss, options); + coll->getIndexCatalog()->dropAllIndexes(_opCtx, true); // Insert some documents. int32_t nDocs = 1000; OpDebug* const nullOpDebug = nullptr; for (int32_t i = 0; i < nDocs; ++i) { ASSERT_OK(coll->insertDocument( - &_opCtx, InsertStatement(BSON("_id" << i)), nullOpDebug, true)); + _opCtx, InsertStatement(BSON("_id" << i)), nullOpDebug, true)); } wunit.commit(); + // Request an interrupt. + getGlobalServiceContext()->setKillAllOperations(); + BSONObj indexInfo = BSON("key" << BSON("_id" << 1) << "name" + << "_id_" + << "v" << static_cast<int>(kIndexVersion)); + ASSERT_TRUE(buildIndexInterrupted(indexInfo)); + // only want to interrupt the index build + getGlobalServiceContext()->unsetKillAllOperations(); } - // Request an interrupt. - getGlobalServiceContext()->setKillAllOperations(); - BSONObj indexInfo = BSON("key" << BSON("_id" << 1) << "name" - << "_id_" - << "v" << static_cast<int>(kIndexVersion)); - ASSERT_TRUE(buildIndexInterrupted(indexInfo)); - // only want to interrupt the index build - getGlobalServiceContext()->unsetKillAllOperations(); + regenOpCtx(); + AutoGetOrCreateDb dbRaii(_opCtx, _nss.db(), LockMode::MODE_IX); + boost::optional<Lock::CollectionLock> collLk; + collLk.emplace(_opCtx, _nss, LockMode::MODE_IX); + // The new index is not listed in the index catalog because the index build failed. - ASSERT(!coll->getIndexCatalog()->findIndexByName(&_opCtx, "_id_")); + ASSERT(!collection()->getIndexCatalog()->findIndexByName(_opCtx, "_id_")); } -}; +}; // namespace IndexUpdateTests + +Status IndexBuildBase::createIndex(const BSONObj& indexSpec) { + Lock::DBLock dbLk(_opCtx, _nss.db(), MODE_IX); + Lock::CollectionLock collLk(_opCtx, _nss, MODE_X); -Status IndexBuildBase::createIndex(const std::string& dbname, const BSONObj& indexSpec) { MultiIndexBlock indexer; ON_BLOCK_EXIT([&] { - indexer.cleanUpAfterBuild(&_opCtx, collection(), MultiIndexBlock::kNoopOnCleanUpFn); + indexer.cleanUpAfterBuild(_opCtx, collection(), MultiIndexBlock::kNoopOnCleanUpFn); }); Status status = - indexer.init(&_opCtx, collection(), indexSpec, MultiIndexBlock::kNoopOnInitFn).getStatus(); + indexer.init(_opCtx, collection(), indexSpec, MultiIndexBlock::kNoopOnInitFn).getStatus(); if (status == ErrorCodes::IndexAlreadyExists) { return Status::OK(); } if (!status.isOK()) { return status; } - status = indexer.insertAllDocumentsInCollection(&_opCtx, collection()); + status = indexer.insertAllDocumentsInCollection(_opCtx, collection()); if (!status.isOK()) { return status; } - status = indexer.checkConstraints(&_opCtx); + status = indexer.checkConstraints(_opCtx); if (!status.isOK()) { return status; } - WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(indexer.commit(&_opCtx, + WriteUnitOfWork wunit(_opCtx); + ASSERT_OK(indexer.commit(_opCtx, collection(), MultiIndexBlock::kNoopOnCreateEachFn, MultiIndexBlock::kNoopOnCommitFn)); @@ -320,8 +354,7 @@ Status IndexBuildBase::createIndex(const std::string& dbname, const BSONObj& ind class SimpleCompoundIndex : public IndexBuildBase { public: SimpleCompoundIndex() { - ASSERT_OK(createIndex("unittest", - BSON("name" + ASSERT_OK(createIndex(BSON("name" << "x" << "key" << BSON("x" << 1 << "y" << 1) << "v" << static_cast<int>(kIndexVersion)))); @@ -333,8 +366,7 @@ public: void run() { // Cannot have same key spec with an option different from the existing one. ASSERT_EQUALS(ErrorCodes::IndexOptionsConflict, - createIndex("unittest", - BSON("name" + createIndex(BSON("name" << "x" << "unique" << true << "key" << BSON("x" << 1 << "y" << 1) << "v" << static_cast<int>(kIndexVersion)))); @@ -344,8 +376,7 @@ public: class SameSpecSameOptions : public SimpleCompoundIndex { public: void run() { - ASSERT_OK(createIndex("unittest", - BSON("name" + ASSERT_OK(createIndex(BSON("name" << "x" << "key" << BSON("x" << 1 << "y" << 1) << "v" << static_cast<int>(kIndexVersion)))); @@ -357,8 +388,7 @@ public: void run() { // Cannot create a different index with the same name as the existing one. ASSERT_EQUALS(ErrorCodes::IndexKeySpecsConflict, - createIndex("unittest", - BSON("name" + createIndex(BSON("name" << "x" << "key" << BSON("y" << 1 << "x" << 1) << "v" << static_cast<int>(kIndexVersion)))); @@ -371,8 +401,7 @@ public: class ComplexIndex : public IndexBuildBase { public: ComplexIndex() { - ASSERT_OK(createIndex("unittests", - BSON("name" + ASSERT_OK(createIndex(BSON("name" << "super" << "unique" << 1 << "sparse" << true << "expireAfterSeconds" << 3600 << "key" @@ -389,8 +418,7 @@ public: // the original. This will throw an IndexOptionsConflict as the index already exists under // another name. ASSERT_EQUALS(ErrorCodes::IndexOptionsConflict, - createIndex("unittests", - BSON("name" + createIndex(BSON("name" << "super2" << "expireAfterSeconds" << 3600 << "sparse" << true << "unique" << 1 << "key" @@ -405,8 +433,7 @@ public: void run() { // Exactly the same specs with the existing one, only specified in a different order than // the original, but with the same name. - ASSERT_OK(createIndex("unittests", - BSON("name" + ASSERT_OK(createIndex(BSON("name" << "super" << "expireAfterSeconds" << 3600 << "sparse" << true << "unique" << 1 << "key" @@ -423,8 +450,7 @@ class SameSpecDifferentUnique : public ComplexIndex { public: void run() { ASSERT_EQUALS(ErrorCodes::IndexOptionsConflict, - createIndex("unittest", - BSON("name" + createIndex(BSON("name" << "super2" << "unique" << false << "sparse" << true << "expireAfterSeconds" << 3600 << "key" @@ -438,8 +464,7 @@ class SameSpecDifferentSparse : public ComplexIndex { public: void run() { ASSERT_EQUALS(ErrorCodes::IndexOptionsConflict, - createIndex("unittest", - BSON("name" + createIndex(BSON("name" << "super2" << "unique" << 1 << "sparse" << false << "background" << true << "expireAfterSeconds" << 3600 << "key" @@ -453,8 +478,7 @@ class SameSpecDifferentTTL : public ComplexIndex { public: void run() { ASSERT_EQUALS(ErrorCodes::IndexOptionsConflict, - createIndex("unittest", - BSON("name" + createIndex(BSON("name" << "super2" << "unique" << 1 << "sparse" << true << "expireAfterSeconds" << 2400 << "key" @@ -468,13 +492,13 @@ class StorageEngineOptions : public IndexBuildBase { public: void run() { // "storageEngine" field has to be an object if present. - ASSERT_NOT_OK(createIndex("unittest", _createSpec(12345))); + ASSERT_NOT_OK(createIndex(_createSpec(12345))); // 'storageEngine' must not be empty. - ASSERT_NOT_OK(createIndex("unittest", _createSpec(BSONObj()))); + ASSERT_NOT_OK(createIndex(_createSpec(BSONObj()))); // Every field under "storageEngine" must match a registered storage engine. - ASSERT_NOT_OK(createIndex("unittest", _createSpec(BSON("unknownEngine" << BSONObj())))); + ASSERT_NOT_OK(createIndex(_createSpec(BSON("unknownEngine" << BSONObj())))); // Testing with 'wiredTiger' because the registered storage engine factory // supports custom index options under 'storageEngine'. @@ -483,20 +507,19 @@ public: // Run 'wiredTiger' tests if the storage engine is supported. if (isRegisteredStorageEngine(getGlobalServiceContext(), storageEngineName)) { // Every field under "storageEngine" has to be an object. - ASSERT_NOT_OK(createIndex("unittest", _createSpec(BSON(storageEngineName << 1)))); + ASSERT_NOT_OK(createIndex(_createSpec(BSON(storageEngineName << 1)))); // Storage engine options must pass validation by the storage engine factory. // For 'wiredTiger', embedded document must contain 'configString'. - ASSERT_NOT_OK(createIndex( - "unittest", _createSpec(BSON(storageEngineName << BSON("unknown" << 1))))); + ASSERT_NOT_OK( + createIndex(_createSpec(BSON(storageEngineName << BSON("unknown" << 1))))); // Configuration string for 'wiredTiger' must be a string. - ASSERT_NOT_OK(createIndex( - "unittest", _createSpec(BSON(storageEngineName << BSON("configString" << 1))))); + ASSERT_NOT_OK( + createIndex(_createSpec(BSON(storageEngineName << BSON("configString" << 1))))); // Valid 'wiredTiger' configuration. ASSERT_OK(createIndex( - "unittest", _createSpec(BSON(storageEngineName << BSON("configString" << "block_compressor=zlib"))))); } |