diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2020-05-05 13:42:17 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-05-05 19:52:07 +0000 |
commit | 60b2655e069a30dfa1656809b1ae25d77617292c (patch) | |
tree | fe2472634e5890f6e17b456366d76910446d80df | |
parent | bae19fea072ed8a8bc46db8f787b5474fbbc5f7b (diff) | |
download | mongo-60b2655e069a30dfa1656809b1ae25d77617292c.tar.gz |
SERVER-47319 dropIndexes should not invariant if a similar index was created while yielding locks to abort different indexes
-rw-r--r-- | jstests/noPassthrough/drop_indexes_on_recreated_index.js | 70 | ||||
-rw-r--r-- | src/mongo/db/catalog/drop_indexes.cpp | 52 |
2 files changed, 105 insertions, 17 deletions
diff --git a/jstests/noPassthrough/drop_indexes_on_recreated_index.js b/jstests/noPassthrough/drop_indexes_on_recreated_index.js new file mode 100644 index 00000000000..2f159248e79 --- /dev/null +++ b/jstests/noPassthrough/drop_indexes_on_recreated_index.js @@ -0,0 +1,70 @@ +/** + * Verifies that the dropIndexes command does not invariant if it sees a similar index build + * completed that it successfully aborted. + */ +(function() { +"use strict"; + +load("jstests/libs/fail_point_util.js"); +load("jstests/noPassthrough/libs/index_build.js"); + +const dbName = "test"; +const collName = "dropIndexesOnRecreatedIndex"; + +const conn = MongoRunner.runMongod({}); +assert(conn); + +const db = conn.getDB(dbName); +assert.commandWorked(db.createCollection(collName)); + +const coll = db.getCollection(collName); +assert.commandWorked(coll.insert({a: 1})); + +const indexSpec = { + a: 1 +}; + +IndexBuildTest.pauseIndexBuilds(conn); + +// Start an index build on {a: 1}. +let awaitIndexBuild = IndexBuildTest.startIndexBuild( + conn, coll.getFullName(), indexSpec, {}, [ErrorCodes.IndexBuildAborted]); +IndexBuildTest.waitForIndexBuildToStart(db, collName, "a_1"); + +const failPoint = "hangAfterAbortingIndexes"; +let res = assert.commandWorked(db.adminCommand({configureFailPoint: failPoint, mode: "alwaysOn"})); +let timesEntered = res.count; + +TestData.dbName = dbName; +TestData.collName = collName; +TestData.indexSpec = indexSpec; + +// Abort {a: 1} while it is being built. +let awaitDropIndexes = startParallelShell(() => { + assert.commandWorked(db.getSiblingDB(TestData.dbName) + .getCollection(TestData.collName) + .dropIndexes(TestData.indexSpec)); +}, conn.port); + +// Wait until {a: 1} is aborted, but before the dropIndexes command finishes. +awaitIndexBuild(); +assert.commandWorked(conn.adminCommand({ + waitForFailPoint: failPoint, + timesEntered: timesEntered + 1, + maxTimeMS: kDefaultWaitForFailPointTimeout +})); + +// Recreate {a: 1} and let it finish. +IndexBuildTest.resumeIndexBuilds(conn); +assert.commandWorked(coll.createIndex(indexSpec)); + +// Allow dropIndexes to finish. The dropIndexes should drop the newly recreated {a: 1} index. +assert.commandWorked(db.adminCommand({configureFailPoint: failPoint, mode: "off"})); +awaitDropIndexes(); + +// The collection should only have the _id index. +let indexes = coll.getIndexes(); +assert.eq(1, indexes.length); + +MongoRunner.stopMongod(conn); +}()); diff --git a/src/mongo/db/catalog/drop_indexes.cpp b/src/mongo/db/catalog/drop_indexes.cpp index e3850b0dc6d..c1c843d52b7 100644 --- a/src/mongo/db/catalog/drop_indexes.cpp +++ b/src/mongo/db/catalog/drop_indexes.cpp @@ -51,6 +51,8 @@ namespace mongo { namespace { +MONGO_FAIL_POINT_DEFINE(hangAfterAbortingIndexes); + // Field name in dropIndexes command for indexes to drop. constexpr auto kIndexFieldName = "index"_sd; @@ -346,6 +348,11 @@ Status dropIndexes(OperationContext* opCtx, abortedIndexBuilders.insert( abortedIndexBuilders.end(), justAborted.begin(), justAborted.end()); + if (MONGO_unlikely(hangAfterAbortingIndexes.shouldFail())) { + LOGV2(4731900, "Hanging on hangAfterAbortingIndexes fail point"); + hangAfterAbortingIndexes.pauseWhileSet(); + } + // Take an exclusive lock on the collection now to be able to perform index catalog writes // when removing ready indexes from disk. autoColl.emplace(opCtx, dbAndUUID, MODE_X); @@ -381,25 +388,36 @@ Status dropIndexes(OperationContext* opCtx, } } - // If the "*" wildcard was not specified, verify that all the index names belonging to the - // index builder were aborted. + // Drop any ready indexes that were created while we yielded our locks while aborting using + // similar index specs. if (!isWildcard && !abortedIndexBuilders.empty()) { - // This is necessary to check shard version. - OldClientContext ctx(opCtx, collection->ns().ns()); - - // Iterate through all aborted indexes and verify none of them are ready. This would - // indicate a flaw with the abort logic that allows indexes to complete despite the - // dropIndexes command reporting they were aborted. - auto indexCatalog = collection->getIndexCatalog(); - const bool includeUnfinished = false; - const bool noneReady = std::none_of(indexNames.begin(), indexNames.end(), [&](auto name) { - return indexCatalog->findIndexByName(opCtx, name, includeUnfinished); - }); + return writeConflictRetry(opCtx, "dropIndexes", dbAndUUID.toString(), [&] { + WriteUnitOfWork wuow(opCtx); - invariant(noneReady, - str::stream() << "Found completed indexes despite aborting index build: " - << abortedIndexBuilders.front()); - return Status::OK(); + // This is necessary to check shard version. + OldClientContext ctx(opCtx, collection->ns().ns()); + + // Iterate through all the aborted indexes and drop any indexes that are ready in the + // index catalog. This would indicate that while we yielded our locks during the abort + // phase, a new identical index was created. + auto indexCatalog = collection->getIndexCatalog(); + const bool includeUnfinished = false; + for (const auto& indexName : indexNames) { + auto desc = indexCatalog->findIndexByName(opCtx, indexName, includeUnfinished); + if (!desc) { + // A similar index wasn't created while we yielded the locks during abort. + continue; + } + + Status status = dropIndexByDescriptor(opCtx, collection, indexCatalog, desc); + if (!status.isOK()) { + return status; + } + } + + wuow.commit(); + return Status::OK(); + }); } if (!abortedIndexBuilders.empty()) { |