summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-04-29 20:19:16 -0400
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2019-04-29 20:37:06 -0400
commit54e3e3d5de5a87ca76c98c97fb4e46fdfacf525d (patch)
treee3c794641ba3d3dcc321a062332a95ef3fed2f01 /src/mongo/dbtests
parentabf37f69a45009f44f275e03e2f07d2d496d5b8d (diff)
downloadmongo-54e3e3d5de5a87ca76c98c97fb4e46fdfacf525d.tar.gz
SERVER-30356 Ensure validation reports the appropriate number of fields for missingIndexEntries and extraIndexEntries
Diffstat (limited to 'src/mongo/dbtests')
-rw-r--r--src/mongo/dbtests/validate_tests.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp
index d49fac99aae..1a384682b4c 100644
--- a/src/mongo/dbtests/validate_tests.cpp
+++ b/src/mongo/dbtests/validate_tests.cpp
@@ -1146,6 +1146,270 @@ 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, _nss));
+ coll = _db->createCollection(&_opCtx, _nss);
+ 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());
+ ASSERT_OK(updateStatus);
+ wunit.commit();
+ }
+
+ {
+ ValidateResults results;
+ BSONObjBuilder output;
+
+ lockDb(MODE_IX);
+ std::unique_ptr<Lock::CollectionLock> lock =
+ stdx::make_unique<Lock::CollectionLock>(&_opCtx, _nss, MODE_X);
+
+ Database* db = _autoDb.get()->getDb();
+ ASSERT_OK(db->getCollection(&_opCtx, _nss)
+ ->validate(&_opCtx, kValidateFull, _background, &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, _nss));
+ coll = _db->createCollection(&_opCtx, _nss);
+ 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, _nss, MODE_X);
+
+ Database* db = _autoDb.get()->getDb();
+ ASSERT_OK(db->getCollection(&_opCtx, _nss)
+ ->validate(&_opCtx, kValidateFull, _background, &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, _nss));
+ coll = _db->createCollection(&_opCtx, _nss);
+ 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, _nss, MODE_X);
+
+ Database* db = _autoDb.get()->getDb();
+ ASSERT_OK(db->getCollection(&_opCtx, _nss)
+ ->validate(&_opCtx, kValidateFull, _background, &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:
@@ -1183,6 +1447,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