summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoustafa Maher <m.maher@10gen.com>2021-05-19 01:41:11 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-19 20:31:00 +0000
commita04e6daade40026c2dca545622d082ef6bb8c82c (patch)
treef0b7f0e262ae3c870856e8dba62ed0aab30e1558
parentf99bf23e8e6d88e4ff6800eb24b200fc382e7753 (diff)
downloadmongo-a04e6daade40026c2dca545622d082ef6bb8c82c.tar.gz
SERVER-57010 Check default write concern during replica set reconfig When the feature flag is enabled
-rw-r--r--jstests/replsets/libs/rename_across_dbs.js9
-rw-r--r--jstests/replsets/reconfig_add_remove_arbiter.js9
-rw-r--r--jstests/replsets/reconfig_fails_no_cwwc_set.js62
-rw-r--r--src/mongo/db/read_write_concern_defaults.cpp17
-rw-r--r--src/mongo/db/read_write_concern_defaults.h5
-rw-r--r--src/mongo/db/read_write_concern_defaults_test.cpp39
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp23
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp9
-rw-r--r--src/mongo/db/repl/replication_coordinator_test_fixture.cpp3
-rw-r--r--src/mongo/db/repl/replication_coordinator_test_fixture.h4
11 files changed, 170 insertions, 11 deletions
diff --git a/jstests/replsets/libs/rename_across_dbs.js b/jstests/replsets/libs/rename_across_dbs.js
index a602b51326f..59b0fba4e14 100644
--- a/jstests/replsets/libs/rename_across_dbs.js
+++ b/jstests/replsets/libs/rename_across_dbs.js
@@ -48,6 +48,15 @@ var RenameAcrossDatabasesTest = function(options) {
replTest.startSet();
replTest.initiate();
+ // This test performs a reconfig that will change the implicit default write concern
+ // from {w: "majority"} to {w: 1}. In order for this reconfig to succeed, we must first
+ // set the cluster-wide write concern.
+ assert.commandWorked(replTest.getPrimary().adminCommand({
+ setDefaultRWConcern: 1,
+ defaultWriteConcern: {w: "majority"},
+ writeConcern: {w: "majority"}
+ }));
+
// If provided in 'options', we set the featureCompatibilityVersion. We do this prior to
// adding any other members to the replica set.
if (options.setFeatureCompatibilityVersion) {
diff --git a/jstests/replsets/reconfig_add_remove_arbiter.js b/jstests/replsets/reconfig_add_remove_arbiter.js
index 0ae14b64b0d..7cbb733c306 100644
--- a/jstests/replsets/reconfig_add_remove_arbiter.js
+++ b/jstests/replsets/reconfig_add_remove_arbiter.js
@@ -14,9 +14,16 @@ const replTest = new ReplSetTest({nodes: 2});
replTest.startSet();
replTest.initiate();
+const primary = replTest.getPrimary();
+// This test performs a reconfig that will change the implicit default write concern
+// from {w: "majority"} to {w: 1}. In order for this reconfig to succeed, we must first
+// set the cluster-wide write concern.
+assert.commandWorked(primary.adminCommand(
+ {setDefaultRWConcern: 1, defaultWriteConcern: {w: "majority"}, writeConcern: {w: "majority"}}));
+
jsTestLog('Start arbiter');
const arbiterConn = replTest.add();
-const admin = replTest.getPrimary().getDB('admin');
+const admin = primary.getDB('admin');
const conf = replTest.getReplSetConfigFromNode();
conf.members.push({_id: 2, host: arbiterConn.host, arbiterOnly: true});
conf.version++;
diff --git a/jstests/replsets/reconfig_fails_no_cwwc_set.js b/jstests/replsets/reconfig_fails_no_cwwc_set.js
new file mode 100644
index 00000000000..326f193ffd7
--- /dev/null
+++ b/jstests/replsets/reconfig_fails_no_cwwc_set.js
@@ -0,0 +1,62 @@
+/*
+ * Test that a reconfig that would change the implicit default write concern fails.
+ * In order to perform such a reconfig, users must set a cluster-wide write concern.
+ *
+ * @tags: [requires_fcv_50]
+ */
+
+(function() {
+'use strict';
+
+load("jstests/libs/write_concern_util.js"); // For 'isDefaultWriteConcernMajorityFlagEnabled()'.
+
+const rst = new ReplSetTest({nodes: 2});
+rst.startSet();
+rst.initiate();
+
+const primary = rst.getPrimary();
+
+const featureFlagEnabled = isDefaultWriteConcernMajorityFlagEnabled(primary);
+let cwwc = primary.adminCommand({getDefaultRWConcern: 1});
+if (featureFlagEnabled) {
+ assert(cwwc.hasOwnProperty("defaultWriteConcern"));
+ assert.eq({w: "majority", wtimeout: 0}, cwwc.defaultWriteConcern, tojson(cwwc));
+} else {
+ assert(!cwwc.hasOwnProperty("defaultWriteConcern"));
+}
+
+jsTestLog("Starting arbiter");
+const arbiter = rst.add();
+const config = rst.getReplSetConfigFromNode();
+config.members.push({_id: 2, host: arbiter.host, arbiterOnly: true});
+config.version++;
+const reconfigErrorMsg =
+ "Reconfig attempted to install a config that would change the implicit default write concern";
+
+if (featureFlagEnabled) {
+ // Adding the arbiter would change the implicit default write concern from {w: majority} to
+ // {w:1}, so we fail the reconfig.
+ let res = assert.commandFailed(primary.adminCommand({replSetReconfig: config}));
+ assert.eq(res.code, ErrorCodes.NewReplicaSetConfigurationIncompatible);
+ assert(res.errmsg.includes(reconfigErrorMsg));
+
+ // A force reconfig should also fail.
+ res = assert.commandFailed(primary.adminCommand({replSetReconfig: config, force: true}));
+ assert.eq(res.code, ErrorCodes.NewReplicaSetConfigurationIncompatible);
+ assert(res.errmsg.includes(reconfigErrorMsg));
+
+ assert.commandWorked(primary.adminCommand(
+ {setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}));
+
+ // After setting the cluster-wide write concern, the same reconfig command should succeed.
+ assert.commandWorked(primary.adminCommand({replSetReconfig: config}));
+
+ cwwc = primary.adminCommand({getDefaultRWConcern: 1});
+ assert(cwwc.hasOwnProperty("defaultWriteConcern"));
+ assert.eq({w: 1, wtimeout: 0}, cwwc.defaultWriteConcern, tojson(cwwc));
+} else {
+ assert.commandWorked(primary.adminCommand({replSetReconfig: config}));
+}
+
+rst.stopSet();
+})();
diff --git a/src/mongo/db/read_write_concern_defaults.cpp b/src/mongo/db/read_write_concern_defaults.cpp
index 183805cf64d..c982ef78bda 100644
--- a/src/mongo/db/read_write_concern_defaults.cpp
+++ b/src/mongo/db/read_write_concern_defaults.cpp
@@ -161,6 +161,23 @@ RWConcernDefault ReadWriteConcernDefaults::generateNewCWRWCToBeSavedOnDisk(
return rwc;
}
+bool ReadWriteConcernDefaults::isCWWCSet(OperationContext* opCtx) {
+ // TODO (SERVER-57042): Call getDefault() instead and return writeConcernSource == KGlobal.
+ auto cached = _getDefaultCWRWCFromDisk(opCtx);
+ if (!cached) {
+ return false;
+ }
+
+ auto wc = cached.get().getDefaultWriteConcern();
+ if (!wc) {
+ return false;
+ }
+
+ // CWWC should have "usedDefault" flag set to false, as this flag only set to true if the
+ // default constructed WC is used.
+ return !wc.get().usedDefault;
+}
+
void ReadWriteConcernDefaults::observeDirectWriteToConfigSettings(OperationContext* opCtx,
BSONElement idElem,
boost::optional<BSONObj> newDoc) {
diff --git a/src/mongo/db/read_write_concern_defaults.h b/src/mongo/db/read_write_concern_defaults.h
index 2108cb8c189..1ce216c2b80 100644
--- a/src/mongo/db/read_write_concern_defaults.h
+++ b/src/mongo/db/read_write_concern_defaults.h
@@ -130,6 +130,11 @@ public:
const boost::optional<WriteConcern>& wc);
/**
+ * Returns true if cluster-wide write concern is set and false otherwise.
+ */
+ bool isCWWCSet(OperationContext* opCtx);
+
+ /**
* Invalidates the cached RWC defaults, causing them to be refreshed.
*
* After this call returns, the read methods below (getDefault, getDefaultReadConcern,
diff --git a/src/mongo/db/read_write_concern_defaults_test.cpp b/src/mongo/db/read_write_concern_defaults_test.cpp
index 1d558ff2ed4..73009587628 100644
--- a/src/mongo/db/read_write_concern_defaults_test.cpp
+++ b/src/mongo/db/read_write_concern_defaults_test.cpp
@@ -54,6 +54,10 @@ protected:
return ReadWriteConcernDefaults::get(getServiceContext()).getDefault(_opCtx);
}
+ bool isCWWCSet() {
+ return ReadWriteConcernDefaults::get(getServiceContext()).isCWWCSet(_opCtx);
+ }
+
ReadWriteConcernDefaultsLookupMock _lookupMock;
bool _isDefaultWCMajorityEnabled{
@@ -72,6 +76,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithAbsentCWRWCWithImplicitWC
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
+ ASSERT(!isCWWCSet());
ASSERT(!defaults.getUpdateOpTime());
ASSERT(!defaults.getUpdateWallClockTime());
ASSERT_EQ(Date_t(), defaults.localUpdateWallClockTime());
@@ -89,6 +94,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithAbsentCWRWCWithImplicitWC
// By not calling _lookupMock.setLookupCallReturnValue(), tests _defaults.lookup() returning
// boost::none.
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(defaults.getDefaultWriteConcern());
@@ -105,6 +111,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNeverSetWithImplicit
// _defaults.lookup() returning default constructed RWConcern not boost::none.
_lookupMock.setLookupCallReturnValue({});
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
@@ -124,6 +131,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithCWRWCNeverSetWithImplicit
createDefaults(true /* isImplicitWCMajority */);
// _defaults.lookup() returning default constructed RWConcern not boost::none.
+ ASSERT(!isCWWCSet());
_lookupMock.setLookupCallReturnValue({});
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
@@ -143,6 +151,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithUnsetCWRWCWithImplicitWCW
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
@@ -166,6 +175,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithUnsetCWRWCWithImplicitWCM
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(defaults.getDefaultWriteConcern());
@@ -184,6 +194,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCW1)
repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern));
WriteConcernOptions wc;
wc.wNumNodes = 4;
+ wc.usedDefault = false;
+ wc.usedDefaultW = false;
newDefaults.setDefaultWriteConcern(wc);
newDefaults.setUpdateOpTime(Timestamp(1, 2));
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
@@ -192,6 +204,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCW1)
}
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(isCWWCSet());
auto defaults = getDefault();
ASSERT(defaults.getDefaultReadConcern()->getLevel() ==
repl::ReadConcernLevel::kLocalReadConcern);
@@ -226,6 +239,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetCWRWCWithImplicitWCMaj
}
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(isCWWCSet());
auto defaults = getDefault();
ASSERT(defaults.getDefaultReadConcern()->getLevel() ==
repl::ReadConcernLevel::kLocalReadConcern);
@@ -246,17 +260,14 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWriteConcernSourceImplicitWit
createDefaults(true /* isImplicitWCMajority */);
RWConcernDefault newDefaults;
- WriteConcernOptions wc;
- wc.wNumNodes = 4;
- wc.usedDefault = true;
- wc.usedDefaultW = false;
- newDefaults.setDefaultWriteConcern(wc);
+ newDefaults.setDefaultWriteConcern(WriteConcernOptions());
newDefaults.setUpdateOpTime(Timestamp(1, 2));
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
newDefaults.setDefaultWriteConcernSource(DefaultWriteConcernSourceEnum::kGlobal);
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
// The default write concern source should be set to implicit if wc.usedDefault is true
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
@@ -279,6 +290,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetAndUnSetCWRCWithImplic
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(defaults.getDefaultReadConcern()->getLevel() ==
repl::ReadConcernLevel::kLocalReadConcern);
@@ -298,6 +310,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetAndUnSetCWRCWithImplic
ReadWriteConcernDefaults::get(getServiceContext()).refreshIfNecessary(_opCtx);
+ ASSERT(!isCWWCSet());
defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
@@ -324,7 +337,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetAndUnSetCWRCWithImplic
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
-
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(defaults.getDefaultReadConcern()->getLevel() ==
repl::ReadConcernLevel::kLocalReadConcern);
@@ -343,6 +356,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetAndUnSetCWRCWithImplic
ReadWriteConcernDefaults::get(getServiceContext()).refreshIfNecessary(_opCtx);
+ ASSERT(!isCWWCSet());
defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(defaults.getDefaultWriteConcern());
@@ -358,6 +372,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultLookupFailure) {
createDefaults(true /* isImplicitWCMajority */);
_lookupMock.setLookupCallFailure(Status{ErrorCodes::Error(1234), "foobar"});
ASSERT_THROWS_CODE_AND_WHAT(getDefault(), AssertionException, 1234, "foobar");
+ ASSERT_THROWS_CODE_AND_WHAT(isCWWCSet(), AssertionException, 1234, "foobar");
}
TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallLookup) {
@@ -368,6 +383,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallL
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
@@ -383,6 +399,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallL
repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern));
WriteConcernOptions wc;
wc.wNumNodes = 4;
+ wc.usedDefault = false;
+ wc.usedDefaultW = false;
newDefaults2.setDefaultWriteConcern(wc);
newDefaults2.setUpdateOpTime(Timestamp(3, 4));
newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678));
@@ -391,6 +409,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallL
}
_lookupMock.setLookupCallReturnValue(std::move(newDefaults2));
+ ASSERT(!isCWWCSet());
auto defaults2 = getDefault();
ASSERT(!defaults2.getDefaultReadConcern());
ASSERT(!defaults2.getDefaultWriteConcern());
@@ -411,6 +430,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) {
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
@@ -426,6 +446,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) {
repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern));
WriteConcernOptions wc;
wc.wNumNodes = 4;
+ wc.usedDefault = false;
+ wc.usedDefaultW = false;
newDefaults2.setDefaultWriteConcern(wc);
newDefaults2.setUpdateOpTime(Timestamp(3, 4));
newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678));
@@ -435,6 +457,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) {
_lookupMock.setLookupCallReturnValue(std::move(newDefaults2));
ReadWriteConcernDefaults::get(getServiceContext()).invalidate();
+ ASSERT(isCWWCSet());
auto defaults2 = getDefault();
ASSERT(defaults2.getDefaultReadConcern()->getLevel() ==
repl::ReadConcernLevel::kLocalReadConcern);
@@ -451,6 +474,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithEmptyCacheAndAbsentD
createDefaults(false /* isImplicitWCMajority */);
ReadWriteConcernDefaults::get(getServiceContext()).refreshIfNecessary(_opCtx);
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT(!defaults.getDefaultReadConcern());
ASSERT(!defaults.getDefaultWriteConcern());
@@ -472,6 +496,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithEmptyCacheAndSetDefa
ReadWriteConcernDefaults::get(getServiceContext()).refreshIfNecessary(_opCtx);
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime());
ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch());
@@ -489,6 +514,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithHigherEpoch) {
newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234));
_lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+ ASSERT(!isCWWCSet());
auto defaults = getDefault();
ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime());
ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch());
@@ -503,6 +529,7 @@ TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithHigherEpoch) {
ReadWriteConcernDefaults::get(getServiceContext()).refreshIfNecessary(_opCtx);
+ ASSERT(!isCWWCSet());
auto defaults2 = getDefault();
ASSERT_EQ(Timestamp(3, 4), *defaults2.getUpdateOpTime());
ASSERT_EQ(5678, defaults2.getUpdateWallClockTime()->toMillisSinceEpoch());
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 67b2c9f9580..eb8ce074e9e 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -1686,6 +1686,7 @@ env.CppUnitTest(
'topology_coordinator_v1_test.cpp',
],
LIBDEPS=[
+ '$BUILD_DIR/mongo/db/read_write_concern_defaults_mock',
'isself',
'repl_coordinator_impl',
'repl_coordinator_test_fixture',
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index 0fb1400aafe..80b7a39f614 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -3451,6 +3451,29 @@ Status ReplicationCoordinatorImpl::_doReplSetReconfig(OperationContext* opCtx,
return status;
ReplSetConfig newConfig = newConfigStatus.getValue();
+ // If the new config changes the replica set's implicit default write concern, we fail the
+ // reconfig command. This includes force reconfigs, but excludes reconfigs that bump the config
+ // term during step-up. The user should set a cluster-wide write concern and attempt the
+ // reconfig command again. We also need to exclude shard servers from this validation, as shard
+ // servers don't store the cluster-wide write concern.
+ if (!skipSafetyChecks /* skipping step-up reconfig */ &&
+ repl::feature_flags::gDefaultWCMajority.isEnabled(
+ serverGlobalParams.featureCompatibility) &&
+ serverGlobalParams.clusterRole != ClusterRole::ShardServer &&
+ !repl::enableDefaultWriteConcernUpdatesForInitiate.load()) {
+ bool currIDWC = oldConfig.isImplicitDefaultWriteConcernMajority();
+ bool newIDWC = newConfig.isImplicitDefaultWriteConcernMajority();
+ bool isCWWCSet = ReadWriteConcernDefaults::get(opCtx).isCWWCSet(opCtx);
+ if (!isCWWCSet && currIDWC != newIDWC) {
+ return Status(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ str::stream()
+ << "Reconfig attempted to install a config that would change the "
+ "implicit default write concern. Use the setDefaultRWConcern command to "
+ "set a cluster-wide write concern and try the reconfig again.");
+ }
+ }
+
BSONObj oldConfigObj = oldConfig.toBSON();
BSONObj newConfigObj = newConfig.toBSON();
audit::logReplSetReconfig(opCtx->getClient(), &oldConfigObj, &newConfigObj);
diff --git a/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp
index b3fb19795f7..4fdb2403540 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp
@@ -32,6 +32,7 @@
#include "mongo/platform/basic.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/read_write_concern_defaults_cache_lookup_mock.h"
#include "mongo/db/repl/repl_server_parameters_gen.h"
#include "mongo/db/repl/repl_set_config.h"
#include "mongo/db/repl/repl_set_heartbeat_args_v1.h"
@@ -1953,6 +1954,14 @@ TEST_F(ReplCoordReconfigTest, NodesWithNewlyAddedFieldSetHavePriorityZero) {
}
TEST_F(ReplCoordReconfigTest, ArbiterNodesShouldNeverHaveNewlyAddedField) {
+ RWConcernDefault newDefaults;
+ WriteConcernOptions wc;
+ wc.wMode = "majority";
+ wc.usedDefault = false;
+ wc.usedDefaultW = false;
+ newDefaults.setDefaultWriteConcern(wc);
+ lookupMock.setLookupCallReturnValue(std::move(newDefaults));
+
setUpNewlyAddedFieldTest();
auto opCtx = makeOperationContext();
diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
index 802b4e921fb..2c6a45f4d0a 100644
--- a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
+++ b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
@@ -144,8 +144,7 @@ void ReplCoordTest::init() {
// The ReadWriteConcernDefaults decoration on the service context won't always be created,
// so we should manually instantiate it to ensure it exists in our tests.
- ReadWriteConcernDefaults::create(service, _lookupMock.getFetchDefaultsFn());
- enableDefaultWriteConcernUpdatesForInitiate.store(true);
+ ReadWriteConcernDefaults::create(service, lookupMock.getFetchDefaultsFn());
TopologyCoordinator::Options settings;
auto topo = std::make_unique<TopologyCoordinator>(settings);
diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.h b/src/mongo/db/repl/replication_coordinator_test_fixture.h
index 46a81edaff0..4589c82aca5 100644
--- a/src/mongo/db/repl/replication_coordinator_test_fixture.h
+++ b/src/mongo/db/repl/replication_coordinator_test_fixture.h
@@ -320,6 +320,8 @@ protected:
*/
void simulateCatchUpAbort();
+ ReadWriteConcernDefaultsLookupMock lookupMock;
+
private:
std::unique_ptr<ReplicationCoordinatorImpl> _repl;
// Owned by ReplicationCoordinatorImpl
@@ -335,8 +337,6 @@ private:
ReplSettings _settings;
bool _callShutdown = false;
-
- ReadWriteConcernDefaultsLookupMock _lookupMock;
};
} // namespace repl