SERVER-111031 Cluster wide default read/write concerns are not honored during promotion to sharded (#45362)
GitOrigin-RevId: c2d3cf79d6a24bbc97c3e1e21c859cba56b66448
This commit is contained in:
parent
1b2b610f43
commit
5f19c11ae9
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -1648,6 +1648,7 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
|
||||
/jstests/sharding/**/remove_shard_*.js @10gen/server-catalog-and-routing-routing-and-topology @svc-auto-approve-bot
|
||||
/jstests/sharding/**/reporting_compound_wildcard_index_prefixed_by_shard_key.js @10gen/server-catalog-and-routing-ddl @svc-auto-approve-bot
|
||||
/jstests/sharding/**/index_commands_shard_targeting.js @10gen/server-catalog-and-routing-ddl @svc-auto-approve-bot
|
||||
/jstests/sharding/**/shard_identity_rollback.js @10gen/server-catalog-and-routing @svc-auto-approve-bot
|
||||
|
||||
# The following patterns are parsed from ./jstests/sharding/analyze_shard_key/OWNERS.yml
|
||||
/jstests/sharding/analyze_shard_key/**/* @10gen/server-cluster-scalability @svc-auto-approve-bot
|
||||
|
||||
@ -698,6 +698,8 @@ const internalCommandsMap = {
|
||||
testname: "_shardsvrDropDatabase",
|
||||
command: {
|
||||
_shardsvrDropDatabase: 1,
|
||||
// This test relies on failing due to write concern not being majority since running this command on the admin database will hang.
|
||||
writeConcern: {w: 1},
|
||||
},
|
||||
},
|
||||
_shardsvrDropDatabaseParticipant: {
|
||||
|
||||
@ -0,0 +1,240 @@
|
||||
/* Tests default read/write concern value consistency before, during, and after promotion to sharded.
|
||||
* @tags: [
|
||||
* requires_persistence,
|
||||
* ]
|
||||
*/
|
||||
|
||||
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
||||
import {describe, beforeEach, afterEach, it} from "jstests/libs/mochalite.js";
|
||||
import {stopReplicationOnSecondaries, restartReplicationOnSecondaries} from "jstests/libs/write_concern_util.js";
|
||||
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
||||
|
||||
describe("Read/write concern defaults directly against shard servers", function () {
|
||||
beforeEach(() => {
|
||||
let verbosity = {replication: 2, command: 2};
|
||||
this.replSet = new ReplSetTest({
|
||||
nodes: 2,
|
||||
nodeOptions: {
|
||||
setParameter: {logComponentVerbosity: verbosity},
|
||||
},
|
||||
});
|
||||
this.replSet.startSet();
|
||||
this.replSet.initiate();
|
||||
this.cluster = undefined;
|
||||
this.mongos = undefined;
|
||||
|
||||
this.dbName = "test";
|
||||
this.collName = "foo";
|
||||
this.counter = 1;
|
||||
assert.commandWorked(this.replSet.getPrimary().getDB(this.dbName).createCollection(this.collName));
|
||||
|
||||
// This function expects the implicit defaults to be:
|
||||
// defaultWriteConcern: {w: "majority", wtimeout: 0}
|
||||
// defaultReadConcern: {level: "local"}
|
||||
this.checkImplicitDefaults = function () {
|
||||
jsTest.log.info("Stop replication on secondaries");
|
||||
stopReplicationOnSecondaries(this.replSet, false);
|
||||
|
||||
jsTest.log.info("Do a write, it should time out due to missing majority");
|
||||
assert.commandFailedWithCode(
|
||||
this.replSet
|
||||
.getPrimary()
|
||||
.getDB(this.dbName)
|
||||
.runCommand({insert: this.collName, documents: [{x: this.counter}], maxTimeMS: 500}),
|
||||
ErrorCodes.MaxTimeMSExpired,
|
||||
);
|
||||
|
||||
jsTest.log.info("Do a read, it should return the document anyways since the default is local");
|
||||
let docFound = this.replSet
|
||||
.getPrimary()
|
||||
.getDB(this.dbName)
|
||||
.getCollection(this.collName)
|
||||
.count({x: this.counter});
|
||||
assert.eq(1, docFound);
|
||||
|
||||
this.counter++;
|
||||
|
||||
jsTest.log.info("Restart replication and wait for steady");
|
||||
restartReplicationOnSecondaries(this.replSet);
|
||||
this.replSet.awaitReplication();
|
||||
};
|
||||
|
||||
// This function expects the user defaults to be:
|
||||
// defaultWriteConcern: {w: "majority", wtimeout: 500}
|
||||
// defaultReadConcern: {level: "majority"}
|
||||
// These values were chosen both for ease of testing and to ensure we aren't overlapping
|
||||
// with the implicit or empty constructor defaults.
|
||||
this.checkUserSpecifiedDefaults = function () {
|
||||
jsTest.log.info("Stop replication on secondaries");
|
||||
stopReplicationOnSecondaries(this.replSet, false);
|
||||
|
||||
jsTest.log.info("Do a write, it should time out due to missing majority");
|
||||
assert.commandFailedWithCode(
|
||||
this.replSet
|
||||
.getPrimary()
|
||||
.getDB(this.dbName)
|
||||
.runCommand({insert: this.collName, documents: [{x: this.counter}]}),
|
||||
ErrorCodes.WriteConcernTimeout,
|
||||
);
|
||||
|
||||
jsTest.log.info("Do a read, it should not return the document since the read concern is majority");
|
||||
let docFound = this.replSet
|
||||
.getPrimary()
|
||||
.getDB(this.dbName)
|
||||
.getCollection(this.collName)
|
||||
.count({x: this.counter});
|
||||
assert.eq(0, docFound);
|
||||
|
||||
this.counter++;
|
||||
|
||||
jsTest.log.info("Restart replication and wait for steady");
|
||||
restartReplicationOnSecondaries(this.replSet);
|
||||
this.replSet.awaitReplication();
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (this.cluster !== undefined) {
|
||||
this.cluster.stop();
|
||||
}
|
||||
if (this.mongos !== undefined) {
|
||||
MongoRunner.stopMongos(this.mongos);
|
||||
}
|
||||
this.replSet.stopSet();
|
||||
});
|
||||
|
||||
it("Implicit default", () => {
|
||||
jsTest.log.info("Check implicit defaults as a normal replica set");
|
||||
this.checkImplicitDefaults();
|
||||
|
||||
jsTest.log.info("Check implicit defaults when started with --shardsvr");
|
||||
this.replSet.stopSet(null, true, {});
|
||||
this.replSet.startSet({"shardsvr": ""}, true);
|
||||
this.checkImplicitDefaults();
|
||||
|
||||
jsTest.log.info("Check implicit defaults after being added to the cluster");
|
||||
this.cluster = new ShardingTest({shards: 0});
|
||||
assert.commandWorked(this.cluster.s.adminCommand({addShard: this.replSet.getURL()}));
|
||||
// Fetch the sharding metadata so that the write doesn't have to do a refresh.
|
||||
assert.commandWorked(
|
||||
this.replSet.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: this.dbName + "." + this.collName}),
|
||||
);
|
||||
this.checkImplicitDefaults();
|
||||
});
|
||||
|
||||
it("Implicit default for config server promotion", () => {
|
||||
jsTest.log.info("Check implicit defaults as a normal replica set");
|
||||
this.checkImplicitDefaults();
|
||||
|
||||
jsTest.log.info("Check implicit defaults when started with --shardsvr");
|
||||
this.replSet.stopSet(null, true, {});
|
||||
this.replSet.startSet({"configsvr": "", replicaSetConfigShardMaintenanceMode: ""}, true);
|
||||
this.checkImplicitDefaults();
|
||||
|
||||
jsTest.log.info("Check implicit defaults after being added to the cluster");
|
||||
this.mongos = MongoRunner.runMongos({configdb: this.replSet.getURL()});
|
||||
assert.commandWorked(this.mongos.adminCommand({transitionFromDedicatedConfigServer: 1}));
|
||||
// Fetch the sharding metadata so that the write doesn't have to do a refresh.
|
||||
assert.commandWorked(
|
||||
this.replSet.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: this.dbName + "." + this.collName}),
|
||||
);
|
||||
this.checkImplicitDefaults();
|
||||
});
|
||||
|
||||
it("User specified default", () => {
|
||||
jsTest.log.info("Set the defaults to something other than the implicit default");
|
||||
this.cluster = new ShardingTest({shards: 0});
|
||||
let conns = [this.replSet.getPrimary(), this.cluster.s];
|
||||
conns.forEach((conn) => {
|
||||
assert.commandWorked(
|
||||
conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultWriteConcern: {w: "majority", wtimeout: 500},
|
||||
defaultReadConcern: {level: "majority"},
|
||||
writeConcern: {w: "majority"},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
jsTest.log.info("Check user specified defaults as a normal replica set");
|
||||
this.checkUserSpecifiedDefaults();
|
||||
|
||||
jsTest.log.info("Check user specified defaults when started with --shardsvr");
|
||||
this.replSet.stopSet(null, true, {});
|
||||
this.replSet.startSet({"shardsvr": ""}, true);
|
||||
this.replSet.awaitReplication();
|
||||
this.checkUserSpecifiedDefaults();
|
||||
|
||||
jsTest.log.info("Check user specified defaults after being added to the cluster");
|
||||
assert.commandWorked(this.cluster.s.adminCommand({addShard: this.replSet.getURL()}));
|
||||
// Fetch the sharding metadata so that the write doesn't have to do a refresh.
|
||||
assert.commandWorked(
|
||||
this.replSet.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: this.dbName + "." + this.collName}),
|
||||
);
|
||||
this.checkUserSpecifiedDefaults();
|
||||
});
|
||||
|
||||
it("User specified default for config server promotion", () => {
|
||||
jsTest.log.info("Set the defaults to something other than the implicit default");
|
||||
assert.commandWorked(
|
||||
this.replSet.getPrimary().adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultWriteConcern: {w: "majority", wtimeout: 500},
|
||||
defaultReadConcern: {level: "majority"},
|
||||
writeConcern: {w: "majority"},
|
||||
}),
|
||||
);
|
||||
|
||||
jsTest.log.info("Check user specified defaults as a normal replica set");
|
||||
this.checkUserSpecifiedDefaults();
|
||||
|
||||
jsTest.log.info("Check user specified defaults when started with --configsvr");
|
||||
this.replSet.stopSet(null, true, {});
|
||||
this.replSet.startSet({"configsvr": "", replicaSetConfigShardMaintenanceMode: ""}, true);
|
||||
this.replSet.awaitReplication();
|
||||
this.checkUserSpecifiedDefaults();
|
||||
|
||||
jsTest.log.info("Check user specified defaults after being added to the cluster");
|
||||
this.mongos = MongoRunner.runMongos({configdb: this.replSet.getURL()});
|
||||
assert.commandWorked(this.mongos.adminCommand({transitionFromDedicatedConfigServer: 1}));
|
||||
// Fetch the sharding metadata so that the write doesn't have to do a refresh.
|
||||
assert.commandWorked(
|
||||
this.replSet.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: this.dbName + "." + this.collName}),
|
||||
);
|
||||
this.checkUserSpecifiedDefaults();
|
||||
});
|
||||
|
||||
it("Changing the default on the cluster does not change the shard's default", () => {
|
||||
jsTest.log.info("Add the shard to the cluster");
|
||||
this.cluster = new ShardingTest({shards: 0});
|
||||
this.replSet.stopSet(null, true, {});
|
||||
this.replSet.startSet({"shardsvr": ""}, true);
|
||||
this.replSet.awaitReplication();
|
||||
assert.commandWorked(this.cluster.s.adminCommand({addShard: this.replSet.getURL()}));
|
||||
this.replSet.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: this.dbName + "." + this.collName});
|
||||
|
||||
jsTest.log.info("Change the defaults on the config server");
|
||||
assert.commandWorked(
|
||||
this.cluster.s.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultWriteConcern: {w: 1, wtimeout: 0},
|
||||
defaultReadConcern: {level: "majority"},
|
||||
writeConcern: {w: "majority"},
|
||||
}),
|
||||
);
|
||||
|
||||
jsTest.log.info("Check that the defaults are still the implicit ones via direct connection");
|
||||
this.checkImplicitDefaults();
|
||||
|
||||
jsTest.log.info("Verify that the defaults cannot be modified on the shard");
|
||||
assert.commandFailedWithCode(
|
||||
this.replSet.getPrimary().adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultWriteConcern: {w: 1, wtimeout: 0},
|
||||
defaultReadConcern: {level: "majority"},
|
||||
writeConcern: {w: "majority"},
|
||||
}),
|
||||
51301,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -142,3 +142,6 @@ filters:
|
||||
- "index_commands_shard_targeting.js":
|
||||
approvers:
|
||||
- 10gen/server-catalog-and-routing-ddl
|
||||
- "shard_identity_rollback.js":
|
||||
approvers:
|
||||
- 10gen/server-catalog-and-routing
|
||||
|
||||
@ -162,6 +162,7 @@ TestData.skipCheckOrphans = true;
|
||||
toShard.adminCommand({
|
||||
_shardsvrCloneCatalogData: "test",
|
||||
from: fromShard.host,
|
||||
writeConcern: {w: 1},
|
||||
}),
|
||||
ErrorCodes.InvalidOptions,
|
||||
);
|
||||
|
||||
@ -141,7 +141,8 @@ jsTest.log("Testing replSetAbortPrimaryCatchUp");
|
||||
});
|
||||
const stopReplProducerFailPoint1 = configureFailPoint(secondary1Conn, "stopReplProducer");
|
||||
stopReplProducerFailPoint1.wait();
|
||||
runAsAdminUser(primary, {insert: "catch_up", documents: [{_id: 1}]}, "test");
|
||||
// Since we have stopped replication on both secondaries, run this with w: 1
|
||||
runAsAdminUser(primary, {insert: "catch_up", documents: [{_id: 1}], writeConcern: {w: 1}}, "test");
|
||||
// Step up the first secondary, it will enter catch up indefinitely
|
||||
assert.commandWorked(secondary1Conn.adminCommand({replSetStepUp: 1}));
|
||||
|
||||
|
||||
@ -44,8 +44,11 @@ let shardIdentityDoc = {
|
||||
clusterId: ObjectId(),
|
||||
};
|
||||
|
||||
// Do this explicitly with w:1 so that we don't hang waiting for replication that will never happen.
|
||||
assert.commandWorked(
|
||||
priConn.getDB("admin").system.version.update({_id: "shardIdentity"}, shardIdentityDoc, {upsert: true}),
|
||||
priConn
|
||||
.getDB("admin")
|
||||
.system.version.update({_id: "shardIdentity"}, shardIdentityDoc, {upsert: true, writeConcern: {w: 1}}),
|
||||
);
|
||||
|
||||
// Ensure sharding state on the primary was initialized
|
||||
@ -62,8 +65,13 @@ secondaries.forEach(function (secondary) {
|
||||
assert(!res.enabled, tojson(res));
|
||||
});
|
||||
|
||||
// Ensure manually deleting the shardIdentity document is not allowed.
|
||||
assert.writeErrorWithCode(priConn.getDB("admin").system.version.remove({_id: "shardIdentity"}), 40070);
|
||||
// Ensure manually deleting the shardIdentity document is not allowed. We do this with w: 1 because
|
||||
// otherwise we would wait for majority write concern of the prior op which was the write of the
|
||||
// shard identity.
|
||||
assert.writeErrorWithCode(
|
||||
priConn.getDB("admin").system.version.remove({_id: "shardIdentity"}, {writeConcern: {w: 1}}),
|
||||
40070,
|
||||
);
|
||||
|
||||
jsTest.log("shutting down primary");
|
||||
// Shut down the primary so a secondary gets elected that definitely won't have replicated the
|
||||
|
||||
@ -284,7 +284,9 @@ const failureModes = {
|
||||
stopServerReplication(expectTwoPhaseCommit ? st.rs1.getSecondaries() : st.rs0.getSecondaries());
|
||||
|
||||
// Do a write on rs0 through the router outside the transaction to ensure the
|
||||
// transaction will choose a read time that has not been majority committed.
|
||||
// transaction will choose a read time that has not been majority committed. We do this
|
||||
// write with {w: 1} because we stopped replication on secondaries so anything else is
|
||||
// unfulfillable.
|
||||
assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1}));
|
||||
},
|
||||
beforeCommit: noop,
|
||||
@ -329,7 +331,7 @@ const failureModes = {
|
||||
// Any read shard failure should have triggered an implicit abort on all shards.
|
||||
// Note the first shard already received commitTransaction but couldn't majority
|
||||
// commit it, so it should have already committed the transaction.
|
||||
const dummyTxnCmd = addTxnFields({commitTransaction: 1}, lsid, txnNumber);
|
||||
const dummyTxnCmd = addTxnFields({commitTransaction: 1, writeConcern: {w: 1}}, lsid, txnNumber);
|
||||
assert.commandWorked(st.rs0.getPrimary().adminCommand(dummyTxnCmd));
|
||||
assert.commandFailedWithCode(
|
||||
st.rs1.getPrimary().adminCommand(dummyTxnCmd),
|
||||
|
||||
@ -332,8 +332,14 @@ if (!FeatureFlagUtil.isPresentAndEnabled(st.shard1, "ShardAuthoritativeDbMetadat
|
||||
|
||||
stopReplicationOnSecondaries(recoveryShardReplSetTest, false /* changeReplicaSetDefaultWCToLocal */);
|
||||
|
||||
// Do a write on the recovery node to bump the recovery node's system last OpTime.
|
||||
recoveryShardReplSetTest.getPrimary().getDB("dummy").getCollection("dummy").insert({dummy: 1});
|
||||
// Do a write on the recovery node to bump the recovery node's system last OpTime. We do this
|
||||
// write with {w: 1} because we stopped replication on secondaries so anything else is
|
||||
// unfulfillable.
|
||||
recoveryShardReplSetTest
|
||||
.getPrimary()
|
||||
.getDB("dummy")
|
||||
.getCollection("dummy")
|
||||
.insert({dummy: 1}, {writeConcern: {w: 1}});
|
||||
|
||||
// While the recovery shard primary cannot majority commit writes, commitTransaction returns
|
||||
// NoSuchTransaction with a writeConcern error.
|
||||
@ -363,8 +369,14 @@ if (!FeatureFlagUtil.isPresentAndEnabled(st.shard1, "ShardAuthoritativeDbMetadat
|
||||
|
||||
stopReplicationOnSecondaries(recoveryShardReplSetTest, false /* changeReplicaSetDefaultWCToLocal */);
|
||||
|
||||
// Do a write on the recovery node to bump the recovery node's system last OpTime.
|
||||
recoveryShardReplSetTest.getPrimary().getDB("dummy").getCollection("dummy").insert({dummy: 1});
|
||||
// Do a write on the recovery node to bump the recovery node's system last OpTime. We do this
|
||||
// write with {w: 1} because we stopped replication on secondaries so anything else is
|
||||
// unfulfillable.
|
||||
recoveryShardReplSetTest
|
||||
.getPrimary()
|
||||
.getDB("dummy")
|
||||
.getCollection("dummy")
|
||||
.insert({dummy: 1}, {writeConcern: {w: 1}});
|
||||
|
||||
// While the recovery shard primary cannot majority commit writes, commitTransaction returns
|
||||
// ok with a writeConcern error.
|
||||
|
||||
@ -1461,14 +1461,16 @@ StatusWith<repl::ReadConcernArgs> ExecCommandDatabase::_extractReadConcern(
|
||||
"internalClient connection {}",
|
||||
redact(_execContext.getRequest().body.toString())),
|
||||
readConcernArgs.isSpecified());
|
||||
} else if (serverGlobalParams.clusterRole.has(ClusterRole::ShardServer) ||
|
||||
serverGlobalParams.clusterRole.has(ClusterRole::ConfigServer)) {
|
||||
if (!readConcernArgs.isSpecified()) {
|
||||
// TODO: Disabled until after SERVER-44539, to avoid log spam.
|
||||
// LOGV2(21954, "Missing readConcern on {command}", "Missing readConcern "
|
||||
// "for command", "command"_attr = _invocation->definition()->getName());
|
||||
}
|
||||
} else {
|
||||
if ((serverGlobalParams.clusterRole.has(ClusterRole::ShardServer) ||
|
||||
serverGlobalParams.clusterRole.has(ClusterRole::ConfigServer)) &&
|
||||
!readConcernArgs.isSpecified()) {
|
||||
// TODO: Disabled until after SERVER-44539, to avoid log spam.
|
||||
// LOGV2(21954,
|
||||
// "Missing readConcern for command",
|
||||
// "command"_attr = _invocation->definition()->getName());
|
||||
}
|
||||
|
||||
// A member in a regular replica set. Since these servers receive client queries, in
|
||||
// this context empty RC (ie. readConcern: {}) means the same as if absent/unspecified,
|
||||
// which is to apply the CWRWC defaults if present. This means we just test isEmpty(),
|
||||
|
||||
@ -240,7 +240,7 @@ TEST_F(ServiceEntryPointShardServerTest, TestReadConcernClientUnspecifiedNoDefau
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointShardServerTest, TestReadConcernClientUnspecifiedWithDefault) {
|
||||
testReadConcernClientUnspecifiedWithDefault(false);
|
||||
testReadConcernClientUnspecifiedWithDefault();
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointShardServerTest, TestReadConcernClientSuppliedLevelNotAllowed) {
|
||||
@ -275,7 +275,7 @@ TEST_F(ServiceEntryPointShardServerTest, TestWriteConcernClientUnspecifiedNoDefa
|
||||
|
||||
TEST_F(ServiceEntryPointShardServerTest, TestWriteConcernClientUnspecifiedWithDefault) {
|
||||
_replCoordMock->setWriteConcernMajorityShouldJournal(false);
|
||||
testWriteConcernClientUnspecifiedWithDefault(false);
|
||||
testWriteConcernClientUnspecifiedWithDefault();
|
||||
}
|
||||
|
||||
#ifdef MONGO_CONFIG_OTEL
|
||||
@ -364,7 +364,7 @@ TEST_F(ServiceEntryPointReplicaSetTest, TestReadConcernClientUnspecifiedNoDefaul
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointReplicaSetTest, TestReadConcernClientUnspecifiedWithDefault) {
|
||||
testReadConcernClientUnspecifiedWithDefault(true);
|
||||
testReadConcernClientUnspecifiedWithDefault();
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointReplicaSetTest, TestReadConcernClientSuppliedLevelNotAllowed) {
|
||||
@ -399,7 +399,7 @@ TEST_F(ServiceEntryPointReplicaSetTest, TestWriteConcernClientUnspecifiedNoDefau
|
||||
|
||||
TEST_F(ServiceEntryPointReplicaSetTest, TestWriteConcernClientUnspecifiedWithDefault) {
|
||||
_replCoordMock->setWriteConcernMajorityShouldJournal(false);
|
||||
testWriteConcernClientUnspecifiedWithDefault(true);
|
||||
testWriteConcernClientUnspecifiedWithDefault();
|
||||
}
|
||||
|
||||
#ifdef MONGO_CONFIG_OTEL
|
||||
|
||||
@ -239,6 +239,7 @@ mongo_cc_library(
|
||||
],
|
||||
deps = [
|
||||
":sharding_mongod_test_fixture",
|
||||
"//src/mongo/db:read_write_concern_defaults_mock",
|
||||
"//src/mongo/db/global_catalog:sharding_catalog_client_impl",
|
||||
"//src/mongo/db/op_observer",
|
||||
"//src/mongo/db/op_observer:op_observer_impl",
|
||||
|
||||
@ -151,6 +151,8 @@ void ConfigServerTestFixture::setUp() {
|
||||
std::move(specialExec),
|
||||
std::move(shardLocal),
|
||||
std::move(localCatalogClient));
|
||||
|
||||
ReadWriteConcernDefaults::create(getService(), _lookupMock.getFetchDefaultsFn());
|
||||
}
|
||||
|
||||
void ConfigServerTestFixture::tearDown() {
|
||||
|
||||
@ -44,6 +44,7 @@
|
||||
#include "mongo/db/keys_collection_document_gen.h"
|
||||
#include "mongo/db/namespace_string.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
#include "mongo/db/read_write_concern_defaults_cache_lookup_mock.h"
|
||||
#include "mongo/db/shard_role/shard_catalog/catalog_raii.h"
|
||||
#include "mongo/db/sharding_environment/client/shard.h"
|
||||
#include "mongo/db/sharding_environment/grid.h"
|
||||
@ -254,6 +255,9 @@ private:
|
||||
|
||||
// Allows for processing tasks through the NetworkInterfaceMock/ThreadPoolMock subsystem.
|
||||
std::unique_ptr<executor::NetworkTestEnv> _addShardNetworkTestEnv;
|
||||
|
||||
// Allows for commands to not specify a default read/write concern.
|
||||
ReadWriteConcernDefaultsLookupMock _lookupMock;
|
||||
};
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
@ -133,10 +133,9 @@ StatusWith<WriteConcernOptions> extractWriteConcern(OperationContext* opCtx,
|
||||
bool clientSuppliedWriteConcern = !writeConcern.usedDefaultConstructedWC;
|
||||
bool customDefaultWasApplied = false;
|
||||
|
||||
// WriteConcern defaults can only be applied on regular replica set members.
|
||||
// Operations received by shard and config servers should always have WC explicitly specified.
|
||||
bool canApplyDefaultWC = serverGlobalParams.clusterRole.has(ClusterRole::None) &&
|
||||
repl::ReplicationCoordinator::get(opCtx)->getSettings().isReplSet() &&
|
||||
// Though the mongoS should always supply a write concern for shardsvr/configsvr nodes, we still
|
||||
// apply the default here for direct shard operations (or for normal replica set members).
|
||||
bool canApplyDefaultWC = repl::ReplicationCoordinator::get(opCtx)->getSettings().isReplSet() &&
|
||||
(!opCtx->inMultiDocumentTransaction() ||
|
||||
isTransactionCommand(opCtx->getService(), commandName)) &&
|
||||
!opCtx->getClient()->isInDirectClient() && !isInternalClient;
|
||||
|
||||
@ -122,7 +122,7 @@ TEST_F(ServiceEntryPointRouterRoleTest, TestReadConcernClientUnspecifiedNoDefaul
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointRouterRoleTest, TestReadConcernClientUnspecifiedWithDefault) {
|
||||
testReadConcernClientUnspecifiedWithDefault(true);
|
||||
testReadConcernClientUnspecifiedWithDefault();
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointRouterRoleTest, TestReadConcernClientSuppliedLevelNotAllowed) {
|
||||
@ -154,7 +154,7 @@ TEST_F(ServiceEntryPointRouterRoleTest, TestWriteConcernClientUnspecifiedNoDefau
|
||||
}
|
||||
|
||||
TEST_F(ServiceEntryPointRouterRoleTest, TestWriteConcernClientUnspecifiedWithDefault) {
|
||||
testWriteConcernClientUnspecifiedWithDefault(true);
|
||||
testWriteConcernClientUnspecifiedWithDefault();
|
||||
}
|
||||
|
||||
#ifdef MONGO_CONFIG_OTEL
|
||||
|
||||
@ -384,11 +384,8 @@ void ServiceEntryPointTestFixture::testReadConcernClientUnspecifiedNoDefault() {
|
||||
ASSERT_EQ(logs.countTextContaining("Applying default readConcern on command"), 1);
|
||||
}
|
||||
|
||||
void ServiceEntryPointTestFixture::testReadConcernClientUnspecifiedWithDefault(
|
||||
bool expectClusterDefault) {
|
||||
// When the read concern is not specified:
|
||||
// * In the router and replica set cases, a cluster-wide default read concern applies
|
||||
// * In the shard server case, an implicit default applies.
|
||||
void ServiceEntryPointTestFixture::testReadConcernClientUnspecifiedWithDefault() {
|
||||
// When the read concern is not specified, a cluster-wide default read concern applies
|
||||
const auto cmdBSON = BSON(TestCmdSucceedsDefaultRCPermitted::kCommandName << 1);
|
||||
auto opCtx = makeOperationContext();
|
||||
setDefaultReadConcern(opCtx.get(), repl::ReadConcernArgs::kAvailable);
|
||||
@ -397,15 +394,10 @@ void ServiceEntryPointTestFixture::testReadConcernClientUnspecifiedWithDefault(
|
||||
logs.stop();
|
||||
auto readConcernArgs = repl::ReadConcernArgs::get(opCtx.get());
|
||||
|
||||
if (expectClusterDefault) {
|
||||
ASSERT_EQ(readConcernArgs.getLevel(), repl::ReadConcernArgs::kAvailable.getLevel());
|
||||
ASSERT_EQ(readConcernArgs.getProvenance(),
|
||||
ReadWriteConcernProvenance(ReadWriteConcernProvenanceSourceEnum::customDefault));
|
||||
ASSERT_EQ(logs.countTextContaining("Applying default readConcern on command"), 1);
|
||||
} else {
|
||||
ASSERT(readConcernArgs.isImplicitDefault());
|
||||
ASSERT_EQ(logs.countTextContaining("Applying default readConcern on command"), 0);
|
||||
}
|
||||
ASSERT_EQ(readConcernArgs.getLevel(), repl::ReadConcernArgs::kAvailable.getLevel());
|
||||
ASSERT_EQ(readConcernArgs.getProvenance(),
|
||||
ReadWriteConcernProvenance(ReadWriteConcernProvenanceSourceEnum::customDefault));
|
||||
ASSERT_EQ(logs.countTextContaining("Applying default readConcern on command"), 1);
|
||||
}
|
||||
|
||||
void ServiceEntryPointTestFixture::testReadConcernClientSuppliedLevelNotAllowed(
|
||||
@ -542,20 +534,13 @@ void ServiceEntryPointTestFixture::testWriteConcernClientUnspecifiedNoDefault()
|
||||
runWriteConcernTestExpectImplicitDefault(opCtx.get());
|
||||
}
|
||||
|
||||
void ServiceEntryPointTestFixture::testWriteConcernClientUnspecifiedWithDefault(
|
||||
bool expectClusterDefault) {
|
||||
// When the write concern is not specified:
|
||||
// * In the router and replica set cases, a cluster-wide default write concern applies
|
||||
// * In the shard server case, an implicit default applies.
|
||||
void ServiceEntryPointTestFixture::testWriteConcernClientUnspecifiedWithDefault() {
|
||||
// When the write concern is not specified, a cluster-wide default write concern applies
|
||||
auto opCtx = makeOperationContext();
|
||||
auto defaultWCObj = BSON("w" << "majority"
|
||||
<< "wtimeout" << 500);
|
||||
setDefaultWriteConcern(opCtx.get(), defaultWCObj);
|
||||
if (expectClusterDefault) {
|
||||
runWriteConcernTestExpectClusterDefault(opCtx.get());
|
||||
} else {
|
||||
runWriteConcernTestExpectImplicitDefault(opCtx.get());
|
||||
}
|
||||
runWriteConcernTestExpectClusterDefault(opCtx.get());
|
||||
}
|
||||
|
||||
void ServiceEntryPointTestFixture::runWriteConcernTestExpectImplicitDefault(
|
||||
|
||||
@ -99,7 +99,7 @@ public:
|
||||
void testCommandMaxTimeMS();
|
||||
void testOpCtxInterrupt(bool deferHandling);
|
||||
void testReadConcernClientUnspecifiedNoDefault();
|
||||
void testReadConcernClientUnspecifiedWithDefault(bool expectClusterDefault);
|
||||
void testReadConcernClientUnspecifiedWithDefault();
|
||||
void testReadConcernClientSuppliedLevelNotAllowed(bool exceptionLogged);
|
||||
void testReadConcernClientSuppliedAllowed();
|
||||
void testReadConcernExtractedOnException();
|
||||
@ -107,7 +107,7 @@ public:
|
||||
void testExhaustCommandNextInvocationSet();
|
||||
void testWriteConcernClientSpecified();
|
||||
void testWriteConcernClientUnspecifiedNoDefault();
|
||||
void testWriteConcernClientUnspecifiedWithDefault(bool expectClusterDefault);
|
||||
void testWriteConcernClientUnspecifiedWithDefault();
|
||||
|
||||
#ifdef MONGO_CONFIG_OTEL
|
||||
void testTelemetryContextDeserializedFromRequest();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user