diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2019-02-14 13:05:52 -0500 |
---|---|---|
committer | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2019-02-22 23:40:01 -0500 |
commit | cbf90926f4bdc5f3fd6d20291a5e24218e5f77ba (patch) | |
tree | f4b7a8e1c4bc9a06f23ec05f1e5fdbc569a7d305 | |
parent | b5f25acf119fd8b2c6119474f6b9ef13180ccc59 (diff) | |
download | mongo-cbf90926f4bdc5f3fd6d20291a5e24218e5f77ba.tar.gz |
SERVER-39527 Implement IndexBuildsCoordinatorMongod::setCommitQuorum
-rw-r--r-- | jstests/noPassthrough/set_commit_quorum.js | 80 | ||||
-rw-r--r-- | jstests/sharding/database_and_shard_versioning_all_commands.js | 2 | ||||
-rw-r--r-- | src/mongo/db/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/set_index_commit_quorum_command.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/index_builds_coordinator.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/index_builds_coordinator.h | 8 | ||||
-rw-r--r-- | src/mongo/db/index_builds_coordinator_mongod.cpp | 65 | ||||
-rw-r--r-- | src/mongo/db/index_builds_coordinator_mongod.h | 3 | ||||
-rw-r--r-- | src/mongo/db/index_builds_coordinator_mongod_test.cpp | 44 | ||||
-rw-r--r-- | src/mongo/embedded/index_builds_coordinator_embedded.cpp | 3 | ||||
-rw-r--r-- | src/mongo/embedded/index_builds_coordinator_embedded.h | 3 |
11 files changed, 207 insertions, 12 deletions
diff --git a/jstests/noPassthrough/set_commit_quorum.js b/jstests/noPassthrough/set_commit_quorum.js new file mode 100644 index 00000000000..83cbf07bf1c --- /dev/null +++ b/jstests/noPassthrough/set_commit_quorum.js @@ -0,0 +1,80 @@ +/** + * Tests that the commit quorum can be changed during a two-phase index build. + * + * @tags: [requires_replication] + */ +(function() { + load("jstests/noPassthrough/libs/index_build.js"); + load("jstests/libs/check_log.js"); + + const replSet = new ReplSetTest({ + nodes: [ + {}, + { + // Disallow elections on secondary. + rsConfig: { + priority: 0, + votes: 0, + }, + }, + ] + }); + + // Allow the createIndexes command to use the index builds coordinator in single-phase mode. + replSet.startSet({setParameter: {enableIndexBuildsCoordinatorForCreateIndexesCommand: true}}); + replSet.initiate(); + + const testDB = replSet.getPrimary().getDB('test'); + const coll = testDB.twoPhaseIndexBuild; + + const bulk = coll.initializeUnorderedBulkOp(); + const numDocs = 1000; + for (let i = 0; i < numDocs; i++) { + bulk.insert({a: i, b: i}); + } + assert.commandWorked(bulk.execute()); + + assert.commandWorked(testDB.adminCommand( + {configureFailPoint: "hangIndexBuildBeforeBuilding", mode: "alwaysOn"})); + + // Starts parallel shell to run the command that will hang. + const awaitShell = startParallelShell(function() { + // Use the index builds coordinator for a two-phase index build. + assert.commandWorked(db.runCommand({ + twoPhaseCreateIndexes: 'twoPhaseIndexBuild', + indexes: [{key: {a: 1}, name: 'a_1'}], + commitQuorum: "majority" + })); + }, testDB.getMongo().port); + + checkLog.contains(replSet.getPrimary(), "Waiting for index build to complete"); + + // Test setting various commit quorums on the index build in our two node replica set. + assert.commandFailed(testDB.runCommand( + {setIndexCommitQuorum: 'twoPhaseIndexBuild', indexNames: ['a_1'], commitQuorum: 3})); + assert.commandFailed(testDB.runCommand({ + setIndexCommitQuorum: 'twoPhaseIndexBuild', + indexNames: ['a_1'], + commitQuorum: "someTag" + })); + + assert.commandWorked(testDB.runCommand( + {setIndexCommitQuorum: 'twoPhaseIndexBuild', indexNames: ['a_1'], commitQuorum: 0})); + assert.commandWorked(testDB.runCommand( + {setIndexCommitQuorum: 'twoPhaseIndexBuild', indexNames: ['a_1'], commitQuorum: 2})); + assert.commandWorked(testDB.runCommand({ + setIndexCommitQuorum: 'twoPhaseIndexBuild', + indexNames: ['a_1'], + commitQuorum: "majority" + })); + + assert.commandWorked( + testDB.adminCommand({configureFailPoint: "hangIndexBuildBeforeBuilding", mode: "off"})); + + // Wait for the parallel shell to complete. + awaitShell(); + + IndexBuildTest.assertIndexes(coll, 2, ["_id_", "a_1"]); + + replSet.stopSet(); +})(); diff --git a/jstests/sharding/database_and_shard_versioning_all_commands.js b/jstests/sharding/database_and_shard_versioning_all_commands.js index 396c8eb0511..e3e48cd8ef9 100644 --- a/jstests/sharding/database_and_shard_versioning_all_commands.js +++ b/jstests/sharding/database_and_shard_versioning_all_commands.js @@ -388,7 +388,7 @@ serverStatus: {skip: "executes locally on mongos (not sent to any remote node)"}, setIndexCommitQuorum: { skipProfilerCheck: true, - sendsDbVersion: false, + sendsDbVersion: true, sendsShardVersion: true, setUp: function(mongosConn) { // Expects the collection to exist, and doesn't implicitly create it. diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index d549563f498..02de74aa313 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -943,6 +943,8 @@ env.Library( ], LIBDEPS_PRIVATE=[ 'curop', + 'db_raii', + 'index_build_entry_helpers', '$BUILD_DIR/mongo/db/catalog/uuid_catalog', ], ) diff --git a/src/mongo/db/commands/set_index_commit_quorum_command.cpp b/src/mongo/db/commands/set_index_commit_quorum_command.cpp index bdff7263a7c..2c0555442db 100644 --- a/src/mongo/db/commands/set_index_commit_quorum_command.cpp +++ b/src/mongo/db/commands/set_index_commit_quorum_command.cpp @@ -86,8 +86,11 @@ public: using InvocationBase::InvocationBase; void typedRun(OperationContext* opCtx) { - uassertStatusOK(IndexBuildsCoordinator::get(opCtx)->setCommitQuorum( - request().getNamespace(), request().getIndexNames(), request().getCommitQuorum())); + uassertStatusOK( + IndexBuildsCoordinator::get(opCtx)->setCommitQuorum(opCtx, + request().getNamespace(), + request().getIndexNames(), + request().getCommitQuorum())); } private: diff --git a/src/mongo/db/index_builds_coordinator.cpp b/src/mongo/db/index_builds_coordinator.cpp index 81c5cbe868d..56d73c60ef1 100644 --- a/src/mongo/db/index_builds_coordinator.cpp +++ b/src/mongo/db/index_builds_coordinator.cpp @@ -61,6 +61,7 @@ using namespace indexbuildentryhelpers; MONGO_FAIL_POINT_DEFINE(hangAfterIndexBuildFirstDrain); MONGO_FAIL_POINT_DEFINE(hangAfterIndexBuildSecondDrain); MONGO_FAIL_POINT_DEFINE(hangAfterIndexBuildDumpsInsertsFromBulk); +MONGO_FAIL_POINT_DEFINE(hangIndexBuildBeforeBuilding); namespace { @@ -752,6 +753,7 @@ void IndexBuildsCoordinator::_buildIndex(OperationContext* opCtx, // background. { Lock::CollectionLock colLock(opCtx->lockState(), nss.ns(), MODE_IX); + MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangIndexBuildBeforeBuilding); uassertStatusOK( _indexBuildsManager.startBuildingIndex(opCtx, collection, replState->buildUUID)); } diff --git a/src/mongo/db/index_builds_coordinator.h b/src/mongo/db/index_builds_coordinator.h index cb7117376e0..3887e0def46 100644 --- a/src/mongo/db/index_builds_coordinator.h +++ b/src/mongo/db/index_builds_coordinator.h @@ -230,10 +230,12 @@ public: virtual Status voteCommitIndexBuild(const UUID& buildUUID, const HostAndPort& hostAndPort) = 0; /** - * TODO: This is not yet implemented. (This will have to take a collection IS lock to look up - * the collection UUID.) + * Sets a new commit quorum on an index build that manages 'indexNames' on collection 'nss'. + * If the 'newCommitQuorum' is not satisfiable by the current replica set config, then the + * previous commit quorum is kept and the UnsatisfiableCommitQuorum error code is returned. */ - virtual Status setCommitQuorum(const NamespaceString& nss, + virtual Status setCommitQuorum(OperationContext* opCtx, + const NamespaceString& nss, const std::vector<StringData>& indexNames, const CommitQuorumOptions& newCommitQuorum) = 0; diff --git a/src/mongo/db/index_builds_coordinator_mongod.cpp b/src/mongo/db/index_builds_coordinator_mongod.cpp index de5374e8c98..51d14f6eec4 100644 --- a/src/mongo/db/index_builds_coordinator_mongod.cpp +++ b/src/mongo/db/index_builds_coordinator_mongod.cpp @@ -35,6 +35,8 @@ #include "mongo/db/catalog/uuid_catalog.h" #include "mongo/db/curop.h" +#include "mongo/db/db_raii.h" +#include "mongo/db/index_build_entry_helpers.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/util/assert_util.h" @@ -43,6 +45,8 @@ namespace mongo { +using namespace indexbuildentryhelpers; + namespace { /** @@ -227,11 +231,66 @@ Status IndexBuildsCoordinatorMongod::voteCommitIndexBuild(const UUID& buildUUID, return Status::OK(); } -Status IndexBuildsCoordinatorMongod::setCommitQuorum(const NamespaceString& nss, +Status IndexBuildsCoordinatorMongod::setCommitQuorum(OperationContext* opCtx, + const NamespaceString& nss, const std::vector<StringData>& indexNames, const CommitQuorumOptions& newCommitQuorum) { - // TODO: not yet implemented. - return Status::OK(); + if (indexNames.empty()) { + return Status(ErrorCodes::IndexNotFound, + str::stream() + << "Cannot set a new commit quorum on an index build in collection '" + << nss + << "' without providing any indexes."); + } + + AutoGetCollectionForRead autoColl(opCtx, nss); + Collection* collection = autoColl.getCollection(); + if (!collection) { + return Status(ErrorCodes::NamespaceNotFound, + str::stream() << "Collection '" << nss << "' was not found."); + } + + UUID collectionUUID = *collection->uuid(); + + stdx::unique_lock<stdx::mutex> lk(_mutex); + auto collectionIt = _collectionIndexBuilds.find(collectionUUID); + if (collectionIt == _collectionIndexBuilds.end()) { + return Status(ErrorCodes::IndexNotFound, + str::stream() << "No index builds found on collection '" << nss << "'."); + } + + if (!collectionIt->second->hasIndexBuildState(lk, indexNames.front())) { + return Status(ErrorCodes::IndexNotFound, + str::stream() << "Cannot find an index build on collection '" << nss + << "' with the provided index names"); + } + + // Use the first index to get the ReplIndexBuildState. + std::shared_ptr<ReplIndexBuildState> buildState = + collectionIt->second->getIndexBuildState(lk, indexNames.front()); + + // Ensure the ReplIndexBuildState has the same indexes as 'indexNames'. + bool equal = std::equal( + buildState->indexNames.begin(), buildState->indexNames.end(), indexNames.begin()); + if (buildState->indexNames.size() != indexNames.size() || !equal) { + return Status(ErrorCodes::IndexNotFound, + str::stream() << "Provided indexes are not all being " + << "built by the same index builder in collection '" + << nss + << "'."); + } + + // See if the new commit quorum is satisfiable. + auto replCoord = repl::ReplicationCoordinator::get(opCtx); + Status status = replCoord->checkIfCommitQuorumCanBeSatisfied(newCommitQuorum); + if (!status.isOK()) { + return status; + } + + // Persist the new commit quorum for the index build and write it to the collection. + buildState->commitQuorum = newCommitQuorum; + const UUID buildUUID = buildState->buildUUID; + return indexbuildentryhelpers::setCommitQuorum(opCtx, buildUUID, newCommitQuorum); } Status IndexBuildsCoordinatorMongod::_finishScanningPhase() { diff --git a/src/mongo/db/index_builds_coordinator_mongod.h b/src/mongo/db/index_builds_coordinator_mongod.h index a9ee94852db..109e53eb028 100644 --- a/src/mongo/db/index_builds_coordinator_mongod.h +++ b/src/mongo/db/index_builds_coordinator_mongod.h @@ -90,7 +90,8 @@ public: Status voteCommitIndexBuild(const UUID& buildUUID, const HostAndPort& hostAndPort) override; - Status setCommitQuorum(const NamespaceString& nss, + Status setCommitQuorum(OperationContext* opCtx, + const NamespaceString& nss, const std::vector<StringData>& indexNames, const CommitQuorumOptions& newCommitQuorum) override; diff --git a/src/mongo/db/index_builds_coordinator_mongod_test.cpp b/src/mongo/db/index_builds_coordinator_mongod_test.cpp index 566219732f7..5dd6938f730 100644 --- a/src/mongo/db/index_builds_coordinator_mongod_test.cpp +++ b/src/mongo/db/index_builds_coordinator_mongod_test.cpp @@ -406,6 +406,50 @@ TEST_F(IndexBuildsCoordinatorMongodTest, DisallowNewBuildsOnNamespace) { } } +TEST_F(IndexBuildsCoordinatorMongodTest, SetCommitQuorumWithBadArguments) { + _indexBuildsCoord->sleepIndexBuilds_forTestOnly(true); + + CommitQuorumOptions newCommitQuorum("majority"); + + // Pass in an empty index list. + Status status = + _indexBuildsCoord->setCommitQuorum(operationContext(), _testFooNss, {}, newCommitQuorum); + ASSERT_EQUALS(ErrorCodes::IndexNotFound, status); + + // Use an invalid collection namespace. + NamespaceString nss("bad.collection"); + status = _indexBuildsCoord->setCommitQuorum( + operationContext(), nss, {"a_1", "b_1"}, newCommitQuorum); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status); + + // No index builds are happening on the collection. + status = _indexBuildsCoord->setCommitQuorum( + operationContext(), _testFooNss, {"a_1", "b_1"}, newCommitQuorum); + ASSERT_EQUALS(ErrorCodes::IndexNotFound, status); + + // Register an index build on _testFooNss. + auto testFoo1Future = + assertGet(_indexBuildsCoord->startIndexBuild(operationContext(), + _testFooUUID, + makeSpecs(_testFooNss, {"a", "b"}), + UUID::gen(), + IndexBuildProtocol::kTwoPhase, + _indexBuildOptions)); + + // No index with the name "c" is being built. + status = + _indexBuildsCoord->setCommitQuorum(operationContext(), _testFooNss, {"c"}, newCommitQuorum); + ASSERT_EQUALS(ErrorCodes::IndexNotFound, status); + + // Pass in extra indexes not being built by the same index builder. + status = _indexBuildsCoord->setCommitQuorum( + operationContext(), _testFooNss, {"a_1", "b_1", "c_1"}, newCommitQuorum); + ASSERT_EQUALS(ErrorCodes::IndexNotFound, status); + + _indexBuildsCoord->sleepIndexBuilds_forTestOnly(false); + unittest::assertGet(testFoo1Future.getNoThrow()); +} + } // namespace } // namespace mongo diff --git a/src/mongo/embedded/index_builds_coordinator_embedded.cpp b/src/mongo/embedded/index_builds_coordinator_embedded.cpp index 206db80a5bd..9609bb7d32b 100644 --- a/src/mongo/embedded/index_builds_coordinator_embedded.cpp +++ b/src/mongo/embedded/index_builds_coordinator_embedded.cpp @@ -102,7 +102,8 @@ Status IndexBuildsCoordinatorEmbedded::voteCommitIndexBuild(const UUID& buildUUI MONGO_UNREACHABLE; } -Status IndexBuildsCoordinatorEmbedded::setCommitQuorum(const NamespaceString& nss, +Status IndexBuildsCoordinatorEmbedded::setCommitQuorum(OperationContext* opCtx, + const NamespaceString& nss, const std::vector<StringData>& indexNames, const CommitQuorumOptions& newCommitQuorum) { MONGO_UNREACHABLE; diff --git a/src/mongo/embedded/index_builds_coordinator_embedded.h b/src/mongo/embedded/index_builds_coordinator_embedded.h index 718276e8df1..c1085f543aa 100644 --- a/src/mongo/embedded/index_builds_coordinator_embedded.h +++ b/src/mongo/embedded/index_builds_coordinator_embedded.h @@ -74,7 +74,8 @@ public: void signalChangeToSecondaryMode() override; void signalChangeToInitialSyncMode() override; Status voteCommitIndexBuild(const UUID& buildUUID, const HostAndPort& hostAndPort) override; - Status setCommitQuorum(const NamespaceString& nss, + Status setCommitQuorum(OperationContext* opCtx, + const NamespaceString& nss, const std::vector<StringData>& indexNames, const CommitQuorumOptions& newCommitQuorum) override; }; |