summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Williams <louis.williams@mongodb.com>2022-03-08 09:56:12 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-08 10:50:35 +0000
commit303a724f519e8d45d3b5d1d31c1ac509cfabf08c (patch)
tree46211bc3831eb61bcf9657bf8eec33c2fb21aa72
parentc66cf1511e10d79636e974e83918dec8acbc724c (diff)
downloadmongo-303a724f519e8d45d3b5d1d31c1ac509cfabf08c.tar.gz
SERVER-63531 Correct error to indicate that non-voting members can be included in commitQuorum
-rw-r--r--jstests/noPassthrough/commit_quorum_voting_nodes.js83
-rw-r--r--src/mongo/db/catalog/commit_quorum_options.cpp4
-rw-r--r--src/mongo/db/catalog/commit_quorum_options_test.cpp4
-rw-r--r--src/mongo/db/repl/topology_coordinator.cpp2
4 files changed, 88 insertions, 5 deletions
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<decltype(cNumNodes)>(repl::ReplSetConfig::kMaxVotingMembers)) {
+ cNumNodes > static_cast<decltype(cNumNodes)>(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<decltype(numNodes)>(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