summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
8 files changed, 91 insertions, 10 deletions
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