summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHuayu Ouyang <huayu.ouyang@mongodb.com>2021-04-22 22:57:03 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-04-28 17:47:34 +0000
commit75a84d831d04c13ee6d0578f2b22b01e234c9737 (patch)
tree1d345e323aab969240138cbcf04029298275e16f
parentddd0a40a0d53de5b4269db46ef7bf8ffdd765cc2 (diff)
downloadmongo-75a84d831d04c13ee6d0578f2b22b01e234c9737.tar.gz
SERVER-55697 Don't allow removing CWWC once it is set
-rw-r--r--jstests/hooks/run_fuzzer_restore_settings.js11
-rw-r--r--jstests/noPassthrough/read_write_concern_defaults_metrics.js27
-rw-r--r--jstests/replsets/server_status_metrics.js6
-rw-r--r--jstests/sharding/cannot_unset_cluster_wide_write_concern_once_set.js59
-rw-r--r--jstests/sharding/read_write_concern_defaults_application.js7
-rw-r--r--jstests/sharding/read_write_concern_defaults_commands_api.js172
-rw-r--r--src/mongo/db/SConscript4
-rw-r--r--src/mongo/db/read_write_concern_defaults.cpp16
-rw-r--r--src/mongo/db/read_write_concern_defaults_test.cpp63
9 files changed, 222 insertions, 143 deletions
diff --git a/jstests/hooks/run_fuzzer_restore_settings.js b/jstests/hooks/run_fuzzer_restore_settings.js
index 54da47e21f0..9c72058514e 100644
--- a/jstests/hooks/run_fuzzer_restore_settings.js
+++ b/jstests/hooks/run_fuzzer_restore_settings.js
@@ -1,13 +1,20 @@
(function() {
'use strict';
+// Randomly resets the cluster wide write concern to either w:1 or w:majority.
+const defaultWriteConcern =
+ (Math.random() < 0.5) ? {w: 1, wtimeout: 0} : {w: 'majority', wtimeout: 0};
+
// Unsetting read/write settings. This command will also cause the server to refresh and get
-// the new settings. A standalone, mongos or old version will return an error; ignore it.
+// the new settings. A standalone, shard server, or old version will return an error; ignore it.
const result = db.adminCommand({
setDefaultRWConcern: 1,
defaultReadConcern: {},
- defaultWriteConcern: {},
+ defaultWriteConcern: defaultWriteConcern,
writeConcern: {w: 1}
});
assert.commandWorkedOrFailedWithCode(result, [51300, 51301, 40415]);
+if (result.ok) {
+ jsTestLog("Resetting the global cluster wide write concern to " + tojson(defaultWriteConcern));
+}
})();
diff --git a/jstests/noPassthrough/read_write_concern_defaults_metrics.js b/jstests/noPassthrough/read_write_concern_defaults_metrics.js
index 2137fe44ddd..fedd2814a9a 100644
--- a/jstests/noPassthrough/read_write_concern_defaults_metrics.js
+++ b/jstests/noPassthrough/read_write_concern_defaults_metrics.js
@@ -51,19 +51,21 @@ function testServerStatus(conn) {
// When no defaults have been set.
verifyServerStatus(conn, {expectNoDefaultsDocument: true});
- // When only write concern is set.
- assert.commandWorked(
- conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: "majority"}}));
- verifyServerStatus(conn, {expectedWC: {w: "majority", wtimeout: 0}});
-
// When only read concern is set.
- assert.commandWorked(conn.adminCommand({
- setDefaultRWConcern: 1,
- defaultWriteConcern: {},
- defaultReadConcern: {level: "majority"}
- }));
+ assert.commandWorked(
+ conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "majority"}}));
verifyServerStatus(conn, {expectedRC: {level: "majority"}});
+ // When read concern is explicitly unset and write concern is unset.
+ assert.commandWorked(conn.adminCommand(
+ {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {}}));
+ verifyServerStatus(conn, {});
+
+ // When only write concern is set.
+ assert.commandWorked(conn.adminCommand(
+ {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {w: "majority"}}));
+ verifyServerStatus(conn, {expectedWC: {w: "majority", wtimeout: 0}});
+
// When both read and write concern are set.
assert.commandWorked(conn.adminCommand({
setDefaultRWConcern: 1,
@@ -73,11 +75,6 @@ function testServerStatus(conn) {
verifyServerStatus(conn,
{expectedRC: {level: "majority"}, expectedWC: {w: "majority", wtimeout: 0}});
- // When both read and write concern are explicitly unset.
- assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {}}));
- verifyServerStatus(conn, {});
-
// When the defaults document has been deleted.
assert.commandWorked(conn.getDB("config").settings.remove({_id: "ReadWriteConcernDefaults"}));
assert.soon(() => {
diff --git a/jstests/replsets/server_status_metrics.js b/jstests/replsets/server_status_metrics.js
index 12ecc9ece6f..6f0806b0bf2 100644
--- a/jstests/replsets/server_status_metrics.js
+++ b/jstests/replsets/server_status_metrics.js
@@ -194,9 +194,9 @@ var res = testDB.a.insert({x: 1});
assert.commandFailedWithCode(res, ErrorCodes.UnsatisfiableWriteConcern);
assert.eq(res.getWriteConcernError().errInfo.writeConcern.provenance, "customDefault");
-// Unset the default WC.
-assert.commandWorked(
- testDB.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {}, writeConcern: {w: 1}}));
+// Set the default WC back to {w: 1, wtimeout: 0}.
+assert.commandWorked(testDB.adminCommand(
+ {setDefaultRWConcern: 1, defaultWriteConcern: {w: 1, wtimeout: 0}, writeConcern: {w: 1}}));
// Validate counters.
var endGLEMetrics = testDB.serverStatus().metrics.getLastError;
diff --git a/jstests/sharding/cannot_unset_cluster_wide_write_concern_once_set.js b/jstests/sharding/cannot_unset_cluster_wide_write_concern_once_set.js
new file mode 100644
index 00000000000..81d953e8157
--- /dev/null
+++ b/jstests/sharding/cannot_unset_cluster_wide_write_concern_once_set.js
@@ -0,0 +1,59 @@
+/**
+ * Tests that CWWC cannot be unset once it is set.
+ * @tags: [requires_fcv_50]
+ */
+(function() {
+"use strict";
+function runTest(conn) {
+ const featureEnabled = assert
+ .commandWorked(conn.adminCommand(
+ {getParameter: 1, featureFlagDefaultWriteConcernMajority: 1}))
+ .featureFlagDefaultWriteConcernMajority.value;
+ if (!featureEnabled) {
+ jsTestLog("Skipping test because the default WC majority feature flag is disabled");
+ return;
+ }
+ let res = conn.adminCommand({getDefaultRWConcern: 1});
+ assert(!res.hasOwnProperty("defaultWriteConcern"));
+ jsTestLog("Setting the default write concern to empty initially works.");
+ assert.commandWorked(conn.adminCommand({
+ setDefaultRWConcern: 1,
+ defaultWriteConcern: {},
+ }));
+
+ res = conn.adminCommand({getDefaultRWConcern: 1});
+ assert(!res.hasOwnProperty("defaultWriteConcern"));
+
+ jsTestLog("Setting the default write concern.");
+ const newDefaultWriteConcern = {w: 2, wtimeout: 60};
+ assert.commandWorked(conn.adminCommand({
+ setDefaultRWConcern: 1,
+ defaultWriteConcern: newDefaultWriteConcern,
+ }));
+ res = conn.adminCommand({getDefaultRWConcern: 1});
+ assert.eq(res.defaultWriteConcern, newDefaultWriteConcern);
+
+ jsTestLog("Attempting to unset the default write concern should fail.");
+ assert.commandFailedWithCode(conn.adminCommand({
+ setDefaultRWConcern: 1,
+ defaultWriteConcern: {},
+ }),
+ ErrorCodes.IllegalOperation);
+
+ res = conn.adminCommand({getDefaultRWConcern: 1});
+ assert.eq(res.defaultWriteConcern, newDefaultWriteConcern);
+}
+
+const name = jsTestName();
+const rst = new ReplSetTest({name: name, nodes: 2});
+rst.startSet();
+rst.initiate();
+const primary = rst.getPrimary();
+runTest(primary);
+rst.stopSet();
+
+const st = new ShardingTest({name: name});
+const mongos = st.s;
+runTest(mongos);
+st.stop();
+})();
diff --git a/jstests/sharding/read_write_concern_defaults_application.js b/jstests/sharding/read_write_concern_defaults_application.js
index 1afaa4345e6..90eb29549ae 100644
--- a/jstests/sharding/read_write_concern_defaults_application.js
+++ b/jstests/sharding/read_write_concern_defaults_application.js
@@ -968,14 +968,13 @@ function runTests(conn, regularCheckConn, configSvrCheckConn) {
configSvrCheckConn,
{explicitRWC: true, explicitProvenance: true});
- assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {}}));
- runScenario("Scenario: RWC defaults unset, explicit RWC present, absent provenance",
+ assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {}}));
+ runScenario("Scenario: Read concern defaults unset, explicit RWC present, absent provenance",
conn,
regularCheckConn,
configSvrCheckConn,
{explicitRWC: true, explicitProvenance: false});
- runScenario("Scenario: RWC defaults unset, explicit RWC present, explicit provenance",
+ runScenario("Scenario: Read concern defaults unset, explicit RWC present, explicit provenance",
conn,
regularCheckConn,
configSvrCheckConn,
diff --git a/jstests/sharding/read_write_concern_defaults_commands_api.js b/jstests/sharding/read_write_concern_defaults_commands_api.js
index 2e433244f2d..21c30ad99ae 100644
--- a/jstests/sharding/read_write_concern_defaults_commands_api.js
+++ b/jstests/sharding/read_write_concern_defaults_commands_api.js
@@ -1,5 +1,6 @@
// Tests the basic API of the getDefaultRWConcern and setDefaultRWConcern commands and their
// associated persisted state against different topologies.
+// @tags: [requires_fcv_50]
(function() {
"use strict";
@@ -65,6 +66,17 @@ function verifyDefaultRWCommandsInvalidInput(conn) {
}),
ErrorCodes.BadValue);
+ // Empty write concern is not allowed if write concern has already been set.
+ const featureEnabled = assert
+ .commandWorked(conn.adminCommand(
+ {getParameter: 1, featureFlagDefaultWriteConcernMajority: 1}))
+ .featureFlagDefaultWriteConcernMajority.value;
+ if (featureEnabled) {
+ assert.commandFailedWithCode(
+ conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {}}),
+ ErrorCodes.IllegalOperation);
+ }
+
// Invalid read concern.
assert.commandFailedWithCode(conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: 1}),
ErrorCodes.TypeMismatch);
@@ -100,21 +112,6 @@ function verifyDefaultRWCommandsInvalidInput(conn) {
ErrorCodes.BadValue);
}
-// Sets a default read and write concern.
-function setDefaultRWConcern(conn) {
- assert.commandWorked(conn.adminCommand({
- setDefaultRWConcern: 1,
- defaultReadConcern: {level: "local"},
- defaultWriteConcern: {w: 1}
- }));
-}
-
-// Unsets the default read and write concerns.
-function unsetDefaultRWConcern(conn) {
- assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {}}));
-}
-
// Verifies the default responses for the default RWC commands and the default persisted state.
function verifyDefaultState(conn) {
const res = assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1}));
@@ -147,9 +144,9 @@ function verifyDefaultState(conn) {
assert.eq(null, getPersistedRWCDocument(conn));
}
-function verifyDefaultRWCommandsValidInput(conn) {
+function verifyDefaultRWCommandsValidInputOnSuccess(conn) {
//
- // Test parameters for getDefaultRWConcern.
+ // Test getDefaultRWConcern when neither read nor write concern are set.
//
// No parameters is allowed.
@@ -159,121 +156,66 @@ function verifyDefaultRWCommandsValidInput(conn) {
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true}));
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: false}));
- //
- // Test parameters for setDefaultRWConcern.
- //
-
- // Setting only rc is allowed.
- assert.commandWorked(
- conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}}));
- assert.commandWorked(
- conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "majority"}}));
-
- // Setting only wc is allowed.
- assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}}));
- assert.commandWorked(
- conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1, j: false}}));
- assert.commandWorked(
- conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: "majority"}}));
-
- // Setting both wc and rc is allowed.
- assert.commandWorked(conn.adminCommand({
- setDefaultRWConcern: 1,
- defaultWriteConcern: {w: 1},
- defaultReadConcern: {level: "local"}
- }));
-
- // Empty write concern is allowed.
- assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {}}));
-
- // Empty read concern is allowed.
- assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {}}));
-}
-
-// Verifies the responses from successful rwc commands and the persisted state after they complete
-// have the expected format.
-function verifyDefaultRWCommandsOnSuccess(conn) {
- //
- // Test responses for getDefaultRWConcern.
- //
-
- // When neither read nor write concern is set.
- unsetDefaultRWConcern(conn);
- verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
- {expectRC: false, expectWC: false});
-
- // When only read concern is set.
- assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}, defaultWriteConcern: {}}));
- verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
- {expectRC: true, expectWC: false});
-
- // When only write concern is set.
- assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {w: 1}}));
- verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
- {expectRC: false, expectWC: true});
-
- // When both read and write concern are set.
- assert.commandWorked(conn.adminCommand({
- setDefaultRWConcern: 1,
- defaultReadConcern: {level: "local"},
- defaultWriteConcern: {w: 1}
- }));
- verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
- {expectRC: true, expectWC: true});
-
// An inMemory response should contain inMemory=true.
const inMemoryRes =
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true}));
assert.eq(inMemoryRes.inMemory, true, tojson(inMemoryRes));
//
- // Test responses for setDefaultRWConcern and persisted state after.
+ // Test getting and setting read concern.
//
- // When unsetting both read and write concern.
- setDefaultRWConcern(conn);
+ // Test setDefaultRWConcern when only read concern is set.
verifyFields(assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {}})),
- {expectRC: false, expectWC: false});
+ {setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}})),
+ {expectRC: true, expectWC: false});
verifyFields(getPersistedRWCDocument(conn),
- {expectRC: false, expectWC: false, isPersistedDocument: true});
+ {expectRC: true, expectWC: false, isPersistedDocument: true});
+
+ // Test getDefaultRWConcern when only read concern is set.
+ verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
+ {expectRC: true, expectWC: false});
- // When unsetting only read concern.
- setDefaultRWConcern(conn);
+ // Test unsetting read concern.
verifyFields(
assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {}})),
- {expectRC: false, expectWC: true});
+ {expectRC: false, expectWC: false});
verifyFields(getPersistedRWCDocument(conn),
- {expectRC: false, expectWC: true, isPersistedDocument: true});
+ {expectRC: false, expectWC: false, isPersistedDocument: true});
+ verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
+ {expectRC: false, expectWC: false});
- // When unsetting only write concern.
- setDefaultRWConcern(conn);
+ //
+ // Test getting and setting write concern.
+ //
+
+ // Empty write concern is allowed if write concern has not already been set.
verifyFields(
assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {}})),
- {expectRC: true, expectWC: false});
+ {expectRC: false, expectWC: false});
verifyFields(getPersistedRWCDocument(conn),
- {expectRC: true, expectWC: false, isPersistedDocument: true});
+ {expectRC: false, expectWC: false, isPersistedDocument: true});
+
+ // Test setRWConcern when only write concern is set.
+ assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}}));
+ assert.commandWorked(
+ conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1, j: false}}));
+ assert.commandWorked(
+ conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: "majority"}}));
- // When setting only write concern.
- unsetDefaultRWConcern(conn);
verifyFields(assert.commandWorked(
conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}})),
{expectRC: false, expectWC: true});
verifyFields(getPersistedRWCDocument(conn),
{expectRC: false, expectWC: true, isPersistedDocument: true});
- // When setting only read concern.
- unsetDefaultRWConcern(conn);
- verifyFields(assert.commandWorked(conn.adminCommand(
- {setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}})),
- {expectRC: true, expectWC: false});
- verifyFields(getPersistedRWCDocument(conn),
- {expectRC: true, expectWC: false, isPersistedDocument: true});
+ // Test getRWConcern when only write concern is set.
+ verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
+ {expectRC: false, expectWC: true});
- // When setting both read and write concern.
- unsetDefaultRWConcern(conn);
+ //
+ // Test getting and setting both read and write concern.
+ //
verifyFields(assert.commandWorked(conn.adminCommand({
setDefaultRWConcern: 1,
defaultReadConcern: {level: "local"},
@@ -282,6 +224,10 @@ function verifyDefaultRWCommandsOnSuccess(conn) {
{expectRC: true, expectWC: true});
verifyFields(getPersistedRWCDocument(conn),
{expectRC: true, expectWC: true, isPersistedDocument: true});
+
+ // Test getRWConcern when both read and write concern are set.
+ verifyFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
+ {expectRC: true, expectWC: true});
}
function getPersistedRWCDocument(conn) {
@@ -315,9 +261,8 @@ jsTestLog("Testing standalone replica set...");
// Primary succeeds.
verifyDefaultState(rst.getPrimary());
- verifyDefaultRWCommandsValidInput(rst.getPrimary());
+ verifyDefaultRWCommandsValidInputOnSuccess(rst.getPrimary());
verifyDefaultRWCommandsInvalidInput(rst.getPrimary());
- verifyDefaultRWCommandsOnSuccess(rst.getPrimary());
// Secondary can run getDefaultRWConcern, but not setDefaultRWConcern.
assert.commandWorked(rst.getSecondary().adminCommand({getDefaultRWConcern: 1}));
@@ -331,13 +276,12 @@ jsTestLog("Testing standalone replica set...");
jsTestLog("Testing sharded cluster...");
{
- const st = new ShardingTest({shards: 1, rs: {nodes: 2}});
+ let st = new ShardingTest({shards: 1, rs: {nodes: 2}});
// Mongos succeeds.
verifyDefaultState(st.s);
- verifyDefaultRWCommandsValidInput(st.s);
+ verifyDefaultRWCommandsValidInputOnSuccess(st.s);
verifyDefaultRWCommandsInvalidInput(st.s);
- verifyDefaultRWCommandsOnSuccess(st.s);
// Shard node fails.
verifyDefaultRWCommandsFailWithCode(st.rs0.getPrimary(), {failureCode: 51301});
@@ -349,10 +293,12 @@ jsTestLog("Testing sharded cluster...");
{setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}}),
ErrorCodes.NotWritablePrimary);
+ st.stop();
+ st = new ShardingTest({shards: 1, rs: {nodes: 2}});
// Config server primary succeeds.
- verifyDefaultRWCommandsValidInput(st.configRS.getPrimary());
+ verifyDefaultState(st.configRS.getPrimary());
+ verifyDefaultRWCommandsValidInputOnSuccess(st.configRS.getPrimary());
verifyDefaultRWCommandsInvalidInput(st.configRS.getPrimary());
- verifyDefaultRWCommandsOnSuccess(st.configRS.getPrimary());
// Config server secondary can run getDefaultRWConcern, but not setDefaultRWConcern.
assert.commandWorked(st.configRS.getSecondary().adminCommand({getDefaultRWConcern: 1}));
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 2556198a1c1..b44f18ffb60 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -432,8 +432,11 @@ env.Library(
'write_concern_options',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/idl/feature_flag',
'$BUILD_DIR/mongo/util/caching',
'$BUILD_DIR/mongo/util/concurrency/thread_pool',
+ 'repl/repl_server_parameters',
+ 'server_options_core',
'vector_clock',
],
)
@@ -2421,6 +2424,7 @@ if wiredtiger:
'repl/mock_repl_coord_server_fixture',
'repl/oplog_interface_local',
'repl/repl_coordinator_interface',
+ 'repl/repl_server_parameters',
'repl/replica_set_aware_service',
'repl/replmocks',
'repl/storage_interface_impl',
diff --git a/src/mongo/db/read_write_concern_defaults.cpp b/src/mongo/db/read_write_concern_defaults.cpp
index 536d3a2471d..a046927eb3b 100644
--- a/src/mongo/db/read_write_concern_defaults.cpp
+++ b/src/mongo/db/read_write_concern_defaults.cpp
@@ -32,7 +32,8 @@
#include "mongo/platform/basic.h"
#include "mongo/db/read_write_concern_defaults.h"
-
+#include "mongo/db/repl/repl_server_parameters_gen.h"
+#include "mongo/db/server_options.h"
#include "mongo/db/vector_clock.h"
#include "mongo/logv2/log.h"
@@ -130,6 +131,19 @@ RWConcernDefault ReadWriteConcernDefaults::generateNewConcerns(
if (!wc && current) {
rwc.setDefaultWriteConcern(current->getDefaultWriteConcern());
}
+ // If the setDefaultRWConcern command tries to unset the global default write concern when it
+ // has already been set, throw an error.
+ // wc->usedDefault indicates that the defaultWriteConcern given in the setDefaultRWConcern
+ // command was empty (i.e. {defaultWriteConcern: {}})
+ // If current->getDefaultWriteConcern exists, that means the global default write concern has
+ // already been set.
+ if (repl::feature_flags::gDefaultWCMajority.isEnabled(
+ serverGlobalParams.featureCompatibility) &&
+ wc && wc->usedDefault && current) {
+ uassert(ErrorCodes::IllegalOperation,
+ str::stream() << "The global default write concern cannot be unset once it is set.",
+ !current->getDefaultWriteConcern());
+ }
return rwc;
}
diff --git a/src/mongo/db/read_write_concern_defaults_test.cpp b/src/mongo/db/read_write_concern_defaults_test.cpp
index c07de0e93f0..e902efbbc52 100644
--- a/src/mongo/db/read_write_concern_defaults_test.cpp
+++ b/src/mongo/db/read_write_concern_defaults_test.cpp
@@ -32,6 +32,8 @@
#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/db/read_write_concern_defaults_cache_lookup_mock.h"
#include "mongo/db/repl/optime.h"
+#include "mongo/db/repl/repl_server_parameters_gen.h"
+#include "mongo/db/server_options.h"
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/db/vector_clock_mutable.h"
#include "mongo/db/vector_clock_test_fixture.h"
@@ -403,13 +405,14 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime,
ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime());
}
-TEST_F(ReadWriteConcernDefaultsTestWithClusterTime,
- TestGenerateNewConcernsValidUnsetReadConcernAndWriteConcern) {
+TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, TestGenerateNewConcernsValidUnsetReadConcern) {
auto oldDefaults = setupOldDefaults();
- auto defaults = _rwcd.generateNewConcerns(
- operationContext(), repl::ReadConcernArgs(), WriteConcernOptions());
+ auto defaults =
+ _rwcd.generateNewConcerns(operationContext(), repl::ReadConcernArgs(), boost::none);
ASSERT(!defaults.getDefaultReadConcern());
- ASSERT(!defaults.getDefaultWriteConcern());
+ ASSERT(defaults.getDefaultWriteConcern());
+ ASSERT_EQ(oldDefaults.getDefaultWriteConcern()->wNumNodes,
+ defaults.getDefaultWriteConcern()->wNumNodes);
ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime());
ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime());
@@ -420,6 +423,56 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime,
}
TEST_F(ReadWriteConcernDefaultsTestWithClusterTime,
+ TestGenerateNewConcernsInvalidUnsetWriteConcern) {
+ auto oldDefaults = setupOldDefaults();
+ if (repl::feature_flags::gDefaultWCMajority.isEnabled(
+ serverGlobalParams.featureCompatibility)) {
+ ASSERT_THROWS_CODE(
+ _rwcd.generateNewConcerns(operationContext(), boost::none, WriteConcernOptions()),
+ AssertionException,
+ ErrorCodes::IllegalOperation);
+ } else {
+ auto defaults =
+ _rwcd.generateNewConcerns(operationContext(), boost::none, WriteConcernOptions());
+ ASSERT(defaults.getDefaultReadConcern());
+ ASSERT(oldDefaults.getDefaultReadConcern()->getLevel() ==
+ defaults.getDefaultReadConcern()->getLevel());
+ ASSERT(!defaults.getDefaultWriteConcern());
+ ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime());
+ ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime());
+
+ _lookupMock.setLookupCallReturnValue(std::move(defaults));
+ _rwcd.refreshIfNecessary(operationContext());
+ auto newDefaults = _rwcd.getDefault(operationContext());
+ ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime());
+ }
+}
+
+TEST_F(ReadWriteConcernDefaultsTestWithClusterTime,
+ TestGenerateNewConcernsInvalidUnsetReadWriteConcern) {
+ auto oldDefaults = setupOldDefaults();
+ if (repl::feature_flags::gDefaultWCMajority.isEnabled(
+ serverGlobalParams.featureCompatibility)) {
+ ASSERT_THROWS_CODE(_rwcd.generateNewConcerns(
+ operationContext(), repl::ReadConcernArgs(), WriteConcernOptions()),
+ AssertionException,
+ ErrorCodes::IllegalOperation);
+ } else {
+ auto defaults = _rwcd.generateNewConcerns(
+ operationContext(), repl::ReadConcernArgs(), WriteConcernOptions());
+ ASSERT(!defaults.getDefaultReadConcern());
+ ASSERT(!defaults.getDefaultWriteConcern());
+ ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime());
+ ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime());
+
+ _lookupMock.setLookupCallReturnValue(std::move(defaults));
+ _rwcd.refreshIfNecessary(operationContext());
+ auto newDefaults = _rwcd.getDefault(operationContext());
+ ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime());
+ }
+}
+
+TEST_F(ReadWriteConcernDefaultsTestWithClusterTime,
TestGenerateNewConcernsValidSetWriteConcernWithOnlyJ) {
auto oldDefaults = setupOldDefaults();
auto defaults =