diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2017-08-09 14:44:24 -0400 |
---|---|---|
committer | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2017-08-09 14:44:33 -0400 |
commit | 336e2bd92d020f4e2f273fa02c388ff1305f89f2 (patch) | |
tree | bd8996b49a33eaf9565b633ddd6f93e6419f064d /src/mongo/dbtests | |
parent | 60ca5426e3903775654c12f874828baf24e05b13 (diff) | |
download | mongo-336e2bd92d020f4e2f273fa02c388ff1305f89f2.tar.gz |
SERVER-30352 Add a new class that will keep track of the index inconsistencies during validation
Diffstat (limited to 'src/mongo/dbtests')
-rw-r--r-- | src/mongo/dbtests/validate_tests.cpp | 297 |
1 files changed, 229 insertions, 68 deletions
diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index 0337435a98e..189ff9e788d 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -58,23 +58,45 @@ static const char* const _ns = "unittests.validate_tests"; */ class ValidateBase { public: - explicit ValidateBase(bool full) : _ctx(&_opCtx, _ns), _client(&_opCtx), _full(full) { + explicit ValidateBase(bool full, bool background) + : _client(&_opCtx), + _full(full), + _background(background), + _nss(_ns), + _autoDb(nullptr), + _db(nullptr) { _client.createCollection(_ns); + { + AutoGetCollection autoGetCollection(&_opCtx, _nss, MODE_X); + _isInRecordIdOrder = + autoGetCollection.getCollection()->getRecordStore()->isInRecordIdOrder(); + } } + ~ValidateBase() { _client.dropCollection(_ns); getGlobalServiceContext()->unsetKillAllOperations(); } - Collection* collection() { - return _ctx.getCollection(); - } protected: bool checkValid() { ValidateResults results; BSONObjBuilder output; - ASSERT_OK(collection()->validate( - &_opCtx, _full ? kValidateFull : kValidateIndex, &results, &output)); + + lockDb(MODE_IX); + invariant(_opCtx.lockState()->isDbLockedForMode(_nss.db(), MODE_IX)); + std::unique_ptr<Lock::CollectionLock> lock = + stdx::make_unique<Lock::CollectionLock>(_opCtx.lockState(), _nss.ns(), MODE_X); + invariant(_opCtx.lockState()->isCollectionLockedForMode(_nss.ns(), MODE_X)); + + Database* db = _autoDb.get()->getDb(); + ASSERT_OK(db->getCollection(&_opCtx, _nss) + ->validate(&_opCtx, + _full ? kValidateFull : kValidateIndex, + _background, + std::move(lock), + &results, + &output)); // Check if errors are reported if and only if valid is set to false. ASSERT_EQ(results.valid, results.errors.empty()); @@ -92,28 +114,52 @@ protected: return results.valid; } + void lockDb(LockMode mode) { + _autoDb.reset(); + invariant(_opCtx.lockState()->isDbLockedForMode(_nss.db(), MODE_NONE)); + _autoDb.reset(new AutoGetDb(&_opCtx, _nss.db().toString(), mode)); + invariant(_opCtx.lockState()->isDbLockedForMode(_nss.db(), mode)); + _db = _autoDb.get()->getDb(); + } + + void releaseDb() { + _autoDb.reset(); + _db = nullptr; + invariant(_opCtx.lockState()->isDbLockedForMode(_nss.db(), MODE_NONE)); + } + const ServiceContext::UniqueOperationContext _txnPtr = cc().makeOperationContext(); OperationContext& _opCtx = *_txnPtr; - OldClientWriteContext _ctx; DBDirectClient _client; bool _full; + bool _background; + const NamespaceString _nss; + unique_ptr<AutoGetDb> _autoDb; + Database* _db; + bool _isInRecordIdOrder; }; -template <bool full> +template <bool full, bool background> class ValidateIdIndexCount : public ValidateBase { public: - ValidateIdIndexCount() : ValidateBase(full) {} + ValidateIdIndexCount() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert records {_id: 1} and {_id: 2} and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); Collection* coll; RecordId id1; { OpDebug* const nullOpDebug = nullptr; WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1)), nullOpDebug, true)); @@ -125,6 +171,7 @@ public: ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Remove {_id: 1} from the record store, so we get more _id entries than records. @@ -136,6 +183,8 @@ public: ASSERT_FALSE(checkValid()); + lockDb(MODE_X); + // Insert records {_id: 0} and {_id: 1} , so we get too few _id entries, and verify // validate fails. { @@ -149,23 +198,30 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; -template <bool full> +template <bool full, bool background> class ValidateSecondaryIndexCount : public ValidateBase { public: - ValidateSecondaryIndexCount() : ValidateBase(full) {} + ValidateSecondaryIndexCount() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert two documents. - Database* db = _ctx.db(); + lockDb(MODE_X); Collection* coll; RecordId id1; { OpDebug* const nullOpDebug = nullptr; WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); id1 = coll->getCursor(&_opCtx)->next()->id; @@ -190,6 +246,7 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Remove a record, so we get more _id entries than records, and verify validate fails. @@ -201,6 +258,8 @@ public: ASSERT_FALSE(checkValid()); + lockDb(MODE_X); + // Insert two more records, so we get too few entries for a non-sparse index, and // verify validate fails. { @@ -214,22 +273,30 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateSecondaryIndex : public ValidateBase { public: - ValidateSecondaryIndex() : ValidateBase(true) {} + ValidateSecondaryIndex() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert three records. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); id1 = coll->getCursor(&_opCtx)->next()->id; @@ -256,6 +323,7 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Update {a: 1} to {a: 9} without updating the index, so we get inconsistent values @@ -271,23 +339,31 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateIdIndex : public ValidateBase { public: - ValidateIdIndex() : ValidateBase(true) {} + ValidateIdIndex() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert records {_id: 1} and {_id: 2} and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1)), nullOpDebug, true)); @@ -299,6 +375,7 @@ public: ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Update {_id: 1} to {_id: 9} without updating the index, so we get inconsistent values @@ -314,6 +391,8 @@ public: ASSERT_FALSE(checkValid()); + lockDb(MODE_X); + // Revert {_id: 9} to {_id: 1} and verify that validate succeeds. { WriteUnitOfWork wunit(&_opCtx); @@ -326,6 +405,8 @@ public: ASSERT_TRUE(checkValid()); + lockDb(MODE_X); + // Remove the {_id: 1} document and insert a new document without an index entry, so there // will still be the same number of index entries and documents, but one document will not // have an index entry. @@ -340,16 +421,24 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateMultiKeyIndex : public ValidateBase { public: - ValidateMultiKeyIndex() : ValidateBase(true) {} + ValidateMultiKeyIndex() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert three records and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; @@ -364,8 +453,8 @@ public: auto doc3 = BSON("_id" << 3 << "a" << BSON_ARRAY(BSON("c" << 1))); { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument(&_opCtx, InsertStatement(doc1), nullOpDebug, true)); @@ -377,6 +466,8 @@ public: ASSERT_TRUE(checkValid()); + lockDb(MODE_X); + // Create multi-key index. auto status = dbtests::createIndexFromSpec(&_opCtx, coll->ns().ns(), @@ -394,6 +485,7 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Update a document's indexed field without updating the index. @@ -407,6 +499,8 @@ public: ASSERT_FALSE(checkValid()); + lockDb(MODE_X); + // Update a document's non-indexed field without updating the index. // Index validation should still be valid. { @@ -418,23 +512,31 @@ public: } ASSERT_TRUE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateSparseIndex : public ValidateBase { public: - ValidateSparseIndex() : ValidateBase(true) {} + ValidateSparseIndex() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert three records and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); @@ -465,6 +567,7 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Update a document's indexed field without updating the index. @@ -478,23 +581,31 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidatePartialIndex : public ValidateBase { public: - ValidatePartialIndex() : ValidateBase(true) {} + ValidatePartialIndex() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert three records and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); @@ -530,6 +641,7 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Update an unindexed document without updating the index. @@ -543,24 +655,32 @@ public: } ASSERT_TRUE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidatePartialIndexOnCollectionWithNonIndexableFields : public ValidateBase { public: - ValidatePartialIndexOnCollectionWithNonIndexableFields() : ValidateBase(true) {} + ValidatePartialIndexOnCollectionWithNonIndexableFields() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection and insert a record that has a non-indexable value on the indexed // field. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK( coll->insertDocument(&_opCtx, InsertStatement(BSON("_id" << 1 << "x" << 1 << "a" << 2)), @@ -606,23 +726,31 @@ public: << BSON("a" << BSON("$eq" << 1)))); ASSERT_OK(status); ASSERT_TRUE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateCompoundIndex : public ValidateBase { public: - ValidateCompoundIndex() : ValidateBase(true) {} + ValidateCompoundIndex() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert five records and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK( coll->insertDocument(&_opCtx, @@ -676,6 +804,7 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); RecordStore* rs = coll->getRecordStore(); // Update a document's indexed field without updating the index. @@ -689,23 +818,31 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateIndexEntry : public ValidateBase { public: - ValidateIndexEntry() : ValidateBase(true) {} + ValidateIndexEntry() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert three records and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); @@ -729,6 +866,8 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); + // Replace a correct index entry with a bad one and check it's invalid. IndexCatalog* indexCatalog = coll->getIndexCatalog(); IndexDescriptor* descriptor = indexCatalog->findIndexByName(&_opCtx, indexName); @@ -754,23 +893,31 @@ public: } ASSERT_FALSE(checkValid()); + releaseDb(); } }; +template <bool full, bool background> class ValidateIndexOrdering : public ValidateBase { public: - ValidateIndexOrdering() : ValidateBase(true) {} + ValidateIndexOrdering() : ValidateBase(full, background) {} void run() { + + // Can't do it in background is the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + // Create a new collection, insert three records and check it's valid. - Database* db = _ctx.db(); + lockDb(MODE_X); OpDebug* const nullOpDebug = nullptr; Collection* coll; RecordId id1; { WriteUnitOfWork wunit(&_opCtx); - ASSERT_OK(db->dropCollection(&_opCtx, _ns)); - coll = db->createCollection(&_opCtx, _ns); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); ASSERT_OK(coll->insertDocument( &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); @@ -794,6 +941,8 @@ public: ASSERT_OK(status); ASSERT_TRUE(checkValid()); + lockDb(MODE_X); + // Change the IndexDescriptor's keyPattern to descending so the index ordering // appears wrong. IndexCatalog* indexCatalog = coll->getIndexCatalog(); @@ -801,6 +950,7 @@ public: descriptor->setKeyPatternForTest(BSON("a" << -1)); ASSERT_FALSE(checkValid()); + releaseDb(); } }; @@ -810,23 +960,34 @@ public: void setupTests() { // Add tests for both full validate and non-full validate. - add<ValidateIdIndexCount<true>>(); - add<ValidateIdIndexCount<false>>(); - add<ValidateSecondaryIndexCount<true>>(); - add<ValidateSecondaryIndexCount<false>>(); - - // These tests are only needed for full validate. - add<ValidateIdIndex>(); - add<ValidateSecondaryIndex>(); - add<ValidateMultiKeyIndex>(); - add<ValidateSparseIndex>(); - add<ValidateCompoundIndex>(); - add<ValidatePartialIndex>(); - add<ValidatePartialIndexOnCollectionWithNonIndexableFields>(); + add<ValidateIdIndexCount<true, false>>(); + add<ValidateIdIndexCount<false, false>>(); + add<ValidateIdIndexCount<false, true>>(); + add<ValidateSecondaryIndexCount<true, false>>(); + add<ValidateSecondaryIndexCount<false, false>>(); + add<ValidateSecondaryIndexCount<false, true>>(); + + // These tests are only needed for non-full validate. + add<ValidateIdIndex<false, false>>(); + add<ValidateIdIndex<false, true>>(); + add<ValidateSecondaryIndex<false, false>>(); + add<ValidateSecondaryIndex<false, true>>(); + add<ValidateMultiKeyIndex<false, false>>(); + add<ValidateMultiKeyIndex<false, true>>(); + add<ValidateSparseIndex<false, false>>(); + add<ValidateSparseIndex<false, true>>(); + add<ValidateCompoundIndex<false, false>>(); + add<ValidateCompoundIndex<false, true>>(); + add<ValidatePartialIndex<false, false>>(); + add<ValidatePartialIndex<false, true>>(); + add<ValidatePartialIndexOnCollectionWithNonIndexableFields<false, false>>(); + add<ValidatePartialIndexOnCollectionWithNonIndexableFields<false, true>>(); // Tests for index validation. - add<ValidateIndexEntry>(); - add<ValidateIndexOrdering>(); + add<ValidateIndexEntry<false, false>>(); + add<ValidateIndexEntry<false, true>>(); + add<ValidateIndexOrdering<false, false>>(); + add<ValidateIndexOrdering<false, true>>(); } } validateTests; } // namespace ValidateTests |