diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2019-04-29 20:19:16 -0400 |
---|---|---|
committer | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2019-05-03 16:03:21 -0400 |
commit | 054a76385000750093fd2132afa82d2f39fc8c65 (patch) | |
tree | 7bb5f4c8bec801756c16a6070e5f0e6480cff2e8 | |
parent | 8bef7b5eb774e9aa4ae86e52ac8c2b3e16d29cef (diff) | |
download | mongo-054a76385000750093fd2132afa82d2f39fc8c65.tar.gz |
SERVER-30356 Ensure validation reports the appropriate number of fields for missingIndexEntries and extraIndexEntries
-rw-r--r-- | src/mongo/dbtests/validate_tests.cpp | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index 87b190dff8f..8c2dd38457a 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -957,6 +957,278 @@ public: } }; +template <bool full, bool background> +class ValidateMissingAndExtraIndexEntryResults : public ValidateBase { +public: + ValidateMissingAndExtraIndexEntryResults() : ValidateBase(full, background) {} + + void run() { + // Can't perform background validation if the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + + // Create a new collection. + lockDb(MODE_X); + Collection* coll; + { + WriteUnitOfWork wunit(&_opCtx); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); + wunit.commit(); + } + + // Create an index. + const auto indexName = "a"; + const auto indexKey = BSON("a" << 1); + auto status = dbtests::createIndexFromSpec( + &_opCtx, + coll->ns().ns(), + BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << indexKey << "v" + << static_cast<int>(kIndexVersion) + << "background" + << false)); + ASSERT_OK(status); + + // Insert documents. + OpDebug* const nullOpDebug = nullptr; + RecordId rid = RecordId::min(); + lockDb(MODE_X); + { + WriteUnitOfWork wunit(&_opCtx); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 2 << "a" << 2)), nullOpDebug, true)); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 3 << "a" << 3)), nullOpDebug, true)); + rid = coll->getCursor(&_opCtx)->next()->id; + wunit.commit(); + } + ASSERT_TRUE(checkValid()); + + RecordStore* rs = coll->getRecordStore(); + + // Updating a document without updating the index entry should cause us to have a missing + // index entry and an extra index entry. + { + WriteUnitOfWork wunit(&_opCtx); + auto doc = BSON("_id" << 1 << "a" << 5); + auto updateStatus = rs->updateRecord( + &_opCtx, rid, doc.objdata(), doc.objsize(), /*enforceQuota*/ false, NULL); + ASSERT_OK(updateStatus); + wunit.commit(); + } + + { + ValidateResults results; + BSONObjBuilder output; + + lockDb(MODE_IX); + std::unique_ptr<Lock::CollectionLock> lock = + stdx::make_unique<Lock::CollectionLock>(_opCtx.lockState(), _nss.ns(), MODE_X); + + Database* db = _autoDb.get()->getDb(); + ASSERT_OK( + db->getCollection(&_opCtx, _nss) + ->validate( + &_opCtx, kValidateFull, _background, std::move(lock), &results, &output)); + + ASSERT_EQ(false, results.valid); + ASSERT_EQ(static_cast<size_t>(1), results.errors.size()); + ASSERT_EQ(static_cast<size_t>(2), results.warnings.size()); + ASSERT_EQ(static_cast<size_t>(1), results.extraIndexEntries.size()); + ASSERT_EQ(static_cast<size_t>(1), results.missingIndexEntries.size()); + } + + releaseDb(); + } +}; + +template <bool full, bool background> +class ValidateMissingIndexEntryResults : public ValidateBase { +public: + ValidateMissingIndexEntryResults() : ValidateBase(full, background) {} + + void run() { + // Can't perform background validation if the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + + // Create a new collection. + lockDb(MODE_X); + Collection* coll; + { + WriteUnitOfWork wunit(&_opCtx); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); + wunit.commit(); + } + + // Create an index. + const auto indexName = "a"; + const auto indexKey = BSON("a" << 1); + auto status = dbtests::createIndexFromSpec( + &_opCtx, + coll->ns().ns(), + BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << indexKey << "v" + << static_cast<int>(kIndexVersion) + << "background" + << false)); + ASSERT_OK(status); + + // Insert documents. + OpDebug* const nullOpDebug = nullptr; + RecordId rid = RecordId::min(); + lockDb(MODE_X); + { + WriteUnitOfWork wunit(&_opCtx); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 2 << "a" << 2)), nullOpDebug, true)); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 3 << "a" << 3)), nullOpDebug, true)); + rid = coll->getCursor(&_opCtx)->next()->id; + wunit.commit(); + } + ASSERT_TRUE(checkValid()); + + // Removing an index entry without removing the document should cause us to have a missing + // index entry. + { + lockDb(MODE_X); + + IndexCatalog* indexCatalog = coll->getIndexCatalog(); + auto descriptor = indexCatalog->findIndexByName(&_opCtx, indexName); + auto iam = + const_cast<IndexAccessMethod*>(indexCatalog->getEntry(descriptor)->accessMethod()); + + WriteUnitOfWork wunit(&_opCtx); + int64_t numDeleted; + const BSONObj actualKey = BSON("a" << 1); + InsertDeleteOptions options; + options.logIfError = true; + options.dupsAllowed = true; + auto removeStatus = iam->remove(&_opCtx, actualKey, rid, options, &numDeleted); + + ASSERT_EQUALS(numDeleted, 1); + ASSERT_OK(removeStatus); + wunit.commit(); + } + + { + ValidateResults results; + BSONObjBuilder output; + + lockDb(MODE_IX); + std::unique_ptr<Lock::CollectionLock> lock = + stdx::make_unique<Lock::CollectionLock>(_opCtx.lockState(), _nss.ns(), MODE_X); + + Database* db = _autoDb.get()->getDb(); + ASSERT_OK( + db->getCollection(&_opCtx, _nss) + ->validate( + &_opCtx, kValidateFull, _background, std::move(lock), &results, &output)); + + ASSERT_EQ(false, results.valid); + ASSERT_EQ(static_cast<size_t>(1), results.errors.size()); + ASSERT_EQ(static_cast<size_t>(1), results.warnings.size()); + ASSERT_EQ(static_cast<size_t>(0), results.extraIndexEntries.size()); + ASSERT_EQ(static_cast<size_t>(1), results.missingIndexEntries.size()); + } + + releaseDb(); + } +}; + +template <bool full, bool background> +class ValidateExtraIndexEntryResults : public ValidateBase { +public: + ValidateExtraIndexEntryResults() : ValidateBase(full, background) {} + + void run() { + // Can't perform background validation if the RecordStore is not in RecordId order. + if (_background && !_isInRecordIdOrder) { + return; + } + + // Create a new collection. + lockDb(MODE_X); + Collection* coll; + { + WriteUnitOfWork wunit(&_opCtx); + ASSERT_OK(_db->dropCollection(&_opCtx, _ns)); + coll = _db->createCollection(&_opCtx, _ns); + wunit.commit(); + } + + // Create an index. + const auto indexName = "a"; + const auto indexKey = BSON("a" << 1); + auto status = dbtests::createIndexFromSpec( + &_opCtx, + coll->ns().ns(), + BSON("name" << indexName << "ns" << coll->ns().ns() << "key" << indexKey << "v" + << static_cast<int>(kIndexVersion) + << "background" + << false)); + ASSERT_OK(status); + + // Insert documents. + OpDebug* const nullOpDebug = nullptr; + RecordId rid = RecordId::min(); + lockDb(MODE_X); + { + WriteUnitOfWork wunit(&_opCtx); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 1 << "a" << 1)), nullOpDebug, true)); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 2 << "a" << 2)), nullOpDebug, true)); + ASSERT_OK(coll->insertDocument( + &_opCtx, InsertStatement(BSON("_id" << 3 << "a" << 3)), nullOpDebug, true)); + rid = coll->getCursor(&_opCtx)->next()->id; + wunit.commit(); + } + ASSERT_TRUE(checkValid()); + + // Removing a document without removing the index entries should cause us to have extra + // index entries. + { + lockDb(MODE_X); + RecordStore* rs = coll->getRecordStore(); + + WriteUnitOfWork wunit(&_opCtx); + rs->deleteRecord(&_opCtx, rid); + wunit.commit(); + } + + { + ValidateResults results; + BSONObjBuilder output; + + lockDb(MODE_IX); + std::unique_ptr<Lock::CollectionLock> lock = + stdx::make_unique<Lock::CollectionLock>(_opCtx.lockState(), _nss.ns(), MODE_X); + + Database* db = _autoDb.get()->getDb(); + ASSERT_OK( + db->getCollection(&_opCtx, _nss) + ->validate( + &_opCtx, kValidateFull, _background, std::move(lock), &results, &output)); + + ASSERT_EQ(false, results.valid); + ASSERT_EQ(static_cast<size_t>(2), results.errors.size()); + ASSERT_EQ(static_cast<size_t>(1), results.warnings.size()); + ASSERT_EQ(static_cast<size_t>(2), results.extraIndexEntries.size()); + ASSERT_EQ(static_cast<size_t>(0), results.missingIndexEntries.size()); + } + + releaseDb(); + } +}; + class ValidateTests : public Suite { public: ValidateTests() : Suite("validate_tests") {} @@ -991,6 +1263,12 @@ public: add<ValidateIndexEntry<false, true>>(); add<ValidateIndexOrdering<false, false>>(); add<ValidateIndexOrdering<false, true>>(); + + // Tests that the 'missingIndexEntries' and 'extraIndexEntries' field are populated + // correctly. + add<ValidateMissingAndExtraIndexEntryResults<false, false>>(); + add<ValidateMissingIndexEntryResults<false, false>>(); + add<ValidateExtraIndexEntryResults<false, false>>(); } } validateTests; } // namespace ValidateTests |