From 303a724f519e8d45d3b5d1d31c1ac509cfabf08c Mon Sep 17 00:00:00 2001 From: Louis Williams Date: Tue, 8 Mar 2022 09:56:12 +0000 Subject: SERVER-63531 Correct error to indicate that non-voting members can be included in commitQuorum --- .../noPassthrough/commit_quorum_voting_nodes.js | 83 ++++++++++++++++++++++ src/mongo/db/catalog/commit_quorum_options.cpp | 4 +- .../db/catalog/commit_quorum_options_test.cpp | 4 +- src/mongo/db/repl/topology_coordinator.cpp | 2 +- 4 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 jstests/noPassthrough/commit_quorum_voting_nodes.js diff --git a/jstests/noPassthrough/commit_quorum_voting_nodes.js b/jstests/noPassthrough/commit_quorum_voting_nodes.js new file mode 100644 index 00000000000..87e7b1ce27a --- /dev/null +++ b/jstests/noPassthrough/commit_quorum_voting_nodes.js @@ -0,0 +1,83 @@ +/** + * Tests that index build commitQuorum can include non-voting data-bearing nodes. + * + * @tags: [ + * requires_journaling, + * requires_persistence, + * requires_replication, + * ] + */ + +(function() { +const replSet = new ReplSetTest({ + nodes: [ + { + rsConfig: { + tags: {es: 'dc1'}, + } + }, + { + rsConfig: { + tags: {es: 'dc2'}, + } + }, + { + // Analytics node with zero votes should be included in a commitQuorum. + rsConfig: { + priority: 0, + votes: 0, + tags: {es: 'analytics'}, + }, + }, + ], +}); + +replSet.startSet(); +replSet.initiate(); + +const primary = replSet.getPrimary(); +const config = primary.getDB('local').system.replset.findOne(); + +// Create a custom write concern for both nodes with the 'es' tag. We will expect this to be usable +// as an option to commitQuorum. +config.settings = { + getLastErrorModes: {ESP: {"es": 3}} +}; +config.version++; +assert.commandWorked(primary.getDB("admin").runCommand({replSetReconfig: config})); + +const db = replSet.getPrimary().getDB('test'); +const coll = db['coll']; +assert.commandWorked(coll.insert({a: 1})); + +// Shut the analytics node down so that it cannot contribute to the commitQuorum. +const analyticsNodeId = 2; +replSet.stop(analyticsNodeId, {forRestart: true}); + +// The default commitQuorum should not include the non-voting analytics node. +assert.commandWorked(coll.createIndex({a: 1})); +coll.dropIndexes(); + +// The explicit "votingMembers" should not include the non-voting analytics node. +assert.commandWorked(coll.createIndex({a: 1}, {}, "votingMembers")); +coll.dropIndexes(); + +// Restart the analytics node down so that it can contribute to the commitQuorum. +replSet.start(analyticsNodeId, {}, true /* restart */); + +// This should include the non-voting analytics node. +const nNodes = replSet.nodeList().length; +assert.commandWorked(coll.createIndex({a: 1}, {}, nNodes)); +coll.dropIndexes(); + +// This custom tag should include the analytics node. +assert.commandWorked(coll.createIndex({a: 1}, {}, "ESP")); +coll.dropIndexes(); + +// Not enough data-bearing nodes to satisfy commit quorum. +assert.commandFailedWithCode(coll.createIndex({a: 1}, {}, nNodes + 1), + ErrorCodes.UnsatisfiableCommitQuorum); +coll.dropIndexes(); + +replSet.stopSet(); +})(); diff --git a/src/mongo/db/catalog/commit_quorum_options.cpp b/src/mongo/db/catalog/commit_quorum_options.cpp index dba101c94e8..c5f575f144f 100644 --- a/src/mongo/db/catalog/commit_quorum_options.cpp +++ b/src/mongo/db/catalog/commit_quorum_options.cpp @@ -68,12 +68,12 @@ Status CommitQuorumOptions::parse(const BSONElement& commitQuorumElement) { if (commitQuorumElement.isNumber()) { auto cNumNodes = commitQuorumElement.safeNumberLong(); if (cNumNodes < 0 || - cNumNodes > static_cast(repl::ReplSetConfig::kMaxVotingMembers)) { + cNumNodes > static_cast(repl::ReplSetConfig::kMaxMembers)) { return Status( ErrorCodes::FailedToParse, str::stream() << "commitQuorum has to be a non-negative number and not greater than " - << repl::ReplSetConfig::kMaxVotingMembers); + << repl::ReplSetConfig::kMaxMembers); } numNodes = static_cast(cNumNodes); } else if (commitQuorumElement.type() == String) { diff --git a/src/mongo/db/catalog/commit_quorum_options_test.cpp b/src/mongo/db/catalog/commit_quorum_options_test.cpp index ad5c3a787bf..e0c6e4d6e4e 100644 --- a/src/mongo/db/catalog/commit_quorum_options_test.cpp +++ b/src/mongo/db/catalog/commit_quorum_options_test.cpp @@ -54,7 +54,7 @@ TEST(CommitQuorumOptionsTest, ParseReturnsFailedToParseIfCommitQuorumIsANegative BSONObj obj = BSON("commitQuorum" << -1); auto status = CommitQuorumOptions().parse(obj.getField("commitQuorum")); ASSERT_EQUALS(ErrorCodes::FailedToParse, status); - ASSERT_EQUALS("commitQuorum has to be a non-negative number and not greater than 7", + ASSERT_EQUALS("commitQuorum has to be a non-negative number and not greater than 50", status.reason()); } @@ -63,7 +63,7 @@ TEST(CommitQuorumOptionsTest, BSONObj obj = BSON("commitQuorum" << 70); auto status = CommitQuorumOptions().parse(obj.getField("commitQuorum")); ASSERT_EQUALS(ErrorCodes::FailedToParse, status); - ASSERT_EQUALS("commitQuorum has to be a non-negative number and not greater than 7", + ASSERT_EQUALS("commitQuorum has to be a non-negative number and not greater than 50", status.reason()); } diff --git a/src/mongo/db/repl/topology_coordinator.cpp b/src/mongo/db/repl/topology_coordinator.cpp index b160effa003..65b780cf877 100644 --- a/src/mongo/db/repl/topology_coordinator.cpp +++ b/src/mongo/db/repl/topology_coordinator.cpp @@ -3666,7 +3666,7 @@ Status TopologyCoordinator::checkIfCommitQuorumCanBeSatisfied( } return {ErrorCodes::UnsatisfiableCommitQuorum, - "Not enough data-bearing voting nodes to satisfy commit quorum"}; + "Not enough data-bearing nodes to satisfy commit quorum"}; } } // namespace repl -- cgit v1.2.1