mirror of https://github.com/mongodb/mongo
SERVER-43720 Add default read write concern commands to mongos and config server
This commit is contained in:
parent
3eecd38739
commit
15d3feea93
|
|
@ -3755,6 +3755,16 @@ var authCommandsLib = {
|
|||
{runOnDb: secondDbName, roles: {}}
|
||||
]
|
||||
},
|
||||
{
|
||||
testname: "getDefaultRWConcern",
|
||||
command: {getDefaultRWConcern: 1},
|
||||
skipUnlessReplicaSet: true,
|
||||
testcases: [
|
||||
{runOnDb: adminDbName, roles: roles_all, privileges: []},
|
||||
{runOnDb: firstDbName, roles: {}},
|
||||
{runOnDb: secondDbName, roles: {}}
|
||||
]
|
||||
},
|
||||
{
|
||||
testname: "getDiagnosticData",
|
||||
command: {getDiagnosticData: 1},
|
||||
|
|
@ -5093,6 +5103,16 @@ var authCommandsLib = {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
testname: "setDefaultRWConcern",
|
||||
command: {setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}},
|
||||
skipUnlessReplicaSet: true,
|
||||
testcases: [
|
||||
{runOnDb: adminDbName, roles: roles_all, privileges: []},
|
||||
{runOnDb: firstDbName, roles: {}},
|
||||
{runOnDb: secondDbName, roles: {}}
|
||||
]
|
||||
},
|
||||
{
|
||||
testname: "setFeatureCompatibilityVersion",
|
||||
command: {setFeatureCompatibilityVersion: "x"},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,326 @@
|
|||
// Tests the basic API of the getDefaultRWConcern and setDefaultRWConcern commands against different
|
||||
// topologies.
|
||||
//
|
||||
// @tags: [requires_fcv_44]
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
// Asserts a set/get default RWC command response contains the expected fields. Assumes a default
|
||||
// read or write concern has been set previously.
|
||||
function verifyResponseFields(res, {expectRC, expectWC}) {
|
||||
// These fields are always set once a read or write concern has been set at least once.
|
||||
const expectedFields = ["epoch", "setTime", "localSetTime"];
|
||||
const unexpectedFields = [];
|
||||
|
||||
if (expectRC) {
|
||||
expectedFields.push("defaultReadConcern");
|
||||
} else {
|
||||
unexpectedFields.push("defaultReadConcern");
|
||||
}
|
||||
|
||||
if (expectWC) {
|
||||
expectedFields.push("defaultWriteConcern");
|
||||
} else {
|
||||
unexpectedFields.push("defaultWriteConcern");
|
||||
}
|
||||
|
||||
assert.hasFields(res, expectedFields);
|
||||
unexpectedFields.forEach(field => {
|
||||
assert(!res.hasOwnProperty(field),
|
||||
`response unexpectedly had field '${field}', res: ${tojson(res)}`);
|
||||
});
|
||||
}
|
||||
|
||||
function verifyDefaultRWCommandsInvalidInput(conn) {
|
||||
//
|
||||
// Test invalid parameters for getDefaultRWConcern.
|
||||
//
|
||||
|
||||
// Invalid inMemory.
|
||||
assert.commandFailedWithCode(conn.adminCommand({getDefaultRWConcern: 1, inMemory: "true"}),
|
||||
ErrorCodes.TypeMismatch);
|
||||
|
||||
//
|
||||
// Test invalid parameters for setDefaultRWConcern.
|
||||
//
|
||||
|
||||
// Must include either wc or rc.
|
||||
assert.commandFailedWithCode(conn.adminCommand({setDefaultRWConcern: 1}), ErrorCodes.BadValue);
|
||||
|
||||
// Invalid write concern.
|
||||
assert.commandFailedWithCode(
|
||||
conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: 1}),
|
||||
ErrorCodes.TypeMismatch);
|
||||
|
||||
// w less than 1.
|
||||
assert.commandFailedWithCode(conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultWriteConcern: {w: 0},
|
||||
}),
|
||||
ErrorCodes.BadValue);
|
||||
|
||||
// Invalid read concern.
|
||||
assert.commandFailedWithCode(conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: 1}),
|
||||
ErrorCodes.TypeMismatch);
|
||||
|
||||
// Non-existent level.
|
||||
assert.commandFailedWithCode(
|
||||
conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "dummy"}}),
|
||||
ErrorCodes.FailedToParse);
|
||||
|
||||
// Unsupported level.
|
||||
assert.commandFailedWithCode(
|
||||
conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "linearizable"}}),
|
||||
ErrorCodes.BadValue);
|
||||
assert.commandFailedWithCode(
|
||||
conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "snapshot"}}),
|
||||
ErrorCodes.BadValue);
|
||||
|
||||
// Fields other than level.
|
||||
assert.commandFailedWithCode(conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultReadConcern: {level: "local", afterClusterTime: Timestamp(50, 1)}
|
||||
}),
|
||||
ErrorCodes.BadValue);
|
||||
assert.commandFailedWithCode(conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultReadConcern: {level: "snapshot", atClusterTime: Timestamp(50, 1)}
|
||||
}),
|
||||
ErrorCodes.BadValue);
|
||||
assert.commandFailedWithCode(conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultReadConcern: {level: "local", afterOpTime: {ts: Timestamp(50, 1), t: 1}}
|
||||
}),
|
||||
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 no fields are returned if neither a default read nor write concern has been set.
|
||||
function verifyDefaultResponses(conn) {
|
||||
const res = assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1}));
|
||||
const inMemoryRes =
|
||||
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true}));
|
||||
|
||||
const unexpectedFields =
|
||||
["defaultReadConcern", "defaultWriteConcern", "epoch", "setTime", "localSetTime"];
|
||||
unexpectedFields.forEach(field => {
|
||||
assert(!res.hasOwnProperty(field),
|
||||
`response unexpectedly had field '${field}', res: ${tojson(res)}`);
|
||||
assert(!inMemoryRes.hasOwnProperty(field),
|
||||
`inMemory=true response unexpectedly had field '${field}', res: ${tojson(res)}`);
|
||||
});
|
||||
}
|
||||
|
||||
function verifyDefaultRWCommandsValidInput(conn) {
|
||||
//
|
||||
// Test parameters for getDefaultRWConcern.
|
||||
//
|
||||
|
||||
// No parameters is allowed.
|
||||
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1}));
|
||||
|
||||
// inMemory parameter is allowed.
|
||||
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: {}}));
|
||||
}
|
||||
|
||||
function verifyDefaultRWCommandsSuccessfulResponses(conn) {
|
||||
//
|
||||
// Test responses for getDefaultRWConcern.
|
||||
//
|
||||
|
||||
// When neither read nor write concern is set.
|
||||
unsetDefaultRWConcern(conn);
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
|
||||
{expectRC: false, expectWC: false});
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true})),
|
||||
{expectRC: false, expectWC: false});
|
||||
|
||||
// When only read concern is set.
|
||||
assert.commandWorked(conn.adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}, defaultWriteConcern: {}}));
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
|
||||
{expectRC: true, expectWC: false});
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true})),
|
||||
{expectRC: true, expectWC: false});
|
||||
|
||||
// When only write concern is set.
|
||||
assert.commandWorked(conn.adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {w: 1}}));
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
|
||||
{expectRC: false, expectWC: true});
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true})),
|
||||
{expectRC: false, expectWC: true});
|
||||
|
||||
// When both read and write concern are set.
|
||||
assert.commandWorked(conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultReadConcern: {level: "local"},
|
||||
defaultWriteConcern: {w: 1}
|
||||
}));
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1})),
|
||||
{expectRC: true, expectWC: true});
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true})),
|
||||
{expectRC: true, expectWC: true});
|
||||
|
||||
//
|
||||
// Test responses for setDefaultRWConcern.
|
||||
//
|
||||
|
||||
// When unsetting both read and write concern.
|
||||
setDefaultRWConcern(conn);
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultReadConcern: {}, defaultWriteConcern: {}})),
|
||||
{expectRC: false, expectWC: false});
|
||||
|
||||
// When unsetting only read concern.
|
||||
setDefaultRWConcern(conn);
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {}})),
|
||||
{expectRC: false, expectWC: true});
|
||||
|
||||
// When unsetting only write concern.
|
||||
setDefaultRWConcern(conn);
|
||||
verifyResponseFields(
|
||||
assert.commandWorked(conn.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {}})),
|
||||
{expectRC: true, expectWC: false});
|
||||
|
||||
// When setting only write concern.
|
||||
unsetDefaultRWConcern(conn);
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}})),
|
||||
{expectRC: false, expectWC: true});
|
||||
|
||||
// When setting only read concern.
|
||||
unsetDefaultRWConcern(conn);
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}})),
|
||||
{expectRC: true, expectWC: false});
|
||||
|
||||
// When setting both read and write concern.
|
||||
unsetDefaultRWConcern(conn);
|
||||
verifyResponseFields(assert.commandWorked(conn.adminCommand({
|
||||
setDefaultRWConcern: 1,
|
||||
defaultReadConcern: {level: "local"},
|
||||
defaultWriteConcern: {w: 1}
|
||||
})),
|
||||
{expectRC: true, expectWC: true});
|
||||
}
|
||||
|
||||
// Verifies the error code returned by connections to nodes that do not support the get/set default
|
||||
// rw concern commands.
|
||||
function verifyDefaultRWCommandsFailWithCode(conn, {failureCode}) {
|
||||
assert.commandFailedWithCode(conn.adminCommand({getDefaultRWConcern: 1}), failureCode);
|
||||
assert.commandFailedWithCode(
|
||||
conn.adminCommand({setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}}),
|
||||
failureCode);
|
||||
}
|
||||
|
||||
jsTestLog("Testing standalone mongod...");
|
||||
{
|
||||
const standalone = MongoRunner.runMongod();
|
||||
|
||||
// Standalone node fails.
|
||||
verifyDefaultRWCommandsFailWithCode(standalone, {failureCode: 51300});
|
||||
|
||||
MongoRunner.stopMongod(standalone);
|
||||
}
|
||||
|
||||
jsTestLog("Testing standalone replica set...");
|
||||
{
|
||||
const rst = new ReplSetTest({nodes: 2});
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
|
||||
// Primary succeeds.
|
||||
verifyDefaultResponses(rst.getPrimary());
|
||||
verifyDefaultRWCommandsValidInput(rst.getPrimary());
|
||||
verifyDefaultRWCommandsInvalidInput(rst.getPrimary());
|
||||
verifyDefaultRWCommandsSuccessfulResponses(rst.getPrimary());
|
||||
|
||||
// Secondary succeeds.
|
||||
assert.commandWorked(rst.getSecondary().adminCommand({getDefaultRWConcern: 1}));
|
||||
// TODO SERVER-44890 Assert setDefaultRWConcern fails with NotMaster instead.
|
||||
assert.commandWorked(rst.getSecondary().adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}}));
|
||||
|
||||
rst.stopSet();
|
||||
}
|
||||
|
||||
jsTestLog("Testing sharded cluster...");
|
||||
{
|
||||
const st = new ShardingTest({shards: 1, rs: {nodes: 2}});
|
||||
|
||||
// Mongos succeeds.
|
||||
verifyDefaultResponses(st.s);
|
||||
verifyDefaultRWCommandsValidInput(st.s);
|
||||
verifyDefaultRWCommandsInvalidInput(st.s);
|
||||
verifyDefaultRWCommandsSuccessfulResponses(st.s);
|
||||
|
||||
// Shard node fails.
|
||||
verifyDefaultRWCommandsFailWithCode(st.rs0.getPrimary(), {failureCode: 51301});
|
||||
verifyDefaultRWCommandsFailWithCode(st.rs0.getSecondary(), {failureCode: 51301});
|
||||
|
||||
// Config server primary succeeds.
|
||||
verifyDefaultRWCommandsValidInput(st.configRS.getPrimary());
|
||||
verifyDefaultRWCommandsInvalidInput(st.configRS.getPrimary());
|
||||
verifyDefaultRWCommandsSuccessfulResponses(st.configRS.getPrimary());
|
||||
|
||||
// Config server secondary succeeds.
|
||||
assert.commandWorked(st.configRS.getSecondary().adminCommand({getDefaultRWConcern: 1}));
|
||||
// TODO SERVER-44890 Assert setDefaultRWConcern fails instead.
|
||||
assert.commandWorked(st.configRS.getSecondary().adminCommand(
|
||||
{setDefaultRWConcern: 1, defaultReadConcern: {level: "local"}}));
|
||||
|
||||
st.stop();
|
||||
}
|
||||
})();
|
||||
|
|
@ -142,7 +142,6 @@ env.Library(
|
|||
'logical_session_server_status_section.cpp',
|
||||
'mr_common.cpp',
|
||||
'reap_logical_session_cache_now.cpp',
|
||||
'rwc_defaults_commands.cpp',
|
||||
'traffic_recording_cmds.cpp',
|
||||
'user_management_commands_common.cpp',
|
||||
env.Idlc('drop_connections.idl')[0],
|
||||
|
|
@ -369,6 +368,7 @@ env.Library(
|
|||
"oplog_note.cpp",
|
||||
"resize_oplog.cpp",
|
||||
"restart_catalog_command.cpp",
|
||||
'rwc_defaults_commands.cpp',
|
||||
"set_feature_compatibility_version_command.cpp",
|
||||
"set_index_commit_quorum_command.cpp",
|
||||
"shutdown_d.cpp",
|
||||
|
|
|
|||
|
|
@ -34,11 +34,24 @@
|
|||
#include "mongo/db/commands.h"
|
||||
#include "mongo/db/commands/rwc_defaults_commands_gen.h"
|
||||
#include "mongo/db/read_write_concern_defaults.h"
|
||||
#include "mongo/db/repl/read_concern_args.h"
|
||||
#include "mongo/db/repl/replication_coordinator.h"
|
||||
#include "mongo/util/log.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace {
|
||||
|
||||
void assertNotStandaloneOrShardServer(OperationContext* opCtx, StringData cmdName) {
|
||||
const auto replCoord = repl::ReplicationCoordinator::get(opCtx);
|
||||
uassert(51300,
|
||||
str::stream() << "'" << cmdName << "' is not supported on standalone nodes.",
|
||||
replCoord->isReplEnabled());
|
||||
|
||||
uassert(51301,
|
||||
str::stream() << "'" << cmdName << "' is not supported on shard nodes.",
|
||||
serverGlobalParams.clusterRole != ClusterRole::ShardServer);
|
||||
}
|
||||
|
||||
class SetDefaultRWConcernCommand : public TypedCommand<SetDefaultRWConcernCommand> {
|
||||
public:
|
||||
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
|
||||
|
|
@ -62,17 +75,11 @@ public:
|
|||
using InvocationBase::InvocationBase;
|
||||
|
||||
auto typedRun(OperationContext* opCtx) {
|
||||
auto rc = request().getDefaultReadConcern();
|
||||
auto wc = request().getDefaultWriteConcern();
|
||||
uassert(ErrorCodes::BadValue,
|
||||
str::stream() << "At least one of the \""
|
||||
<< SetDefaultRWConcern::kDefaultReadConcernFieldName << "\" or \""
|
||||
<< SetDefaultRWConcern::kDefaultWriteConcernFieldName
|
||||
<< "\" fields must be present",
|
||||
rc || wc);
|
||||
assertNotStandaloneOrShardServer(opCtx, SetDefaultRWConcern::kCommandName);
|
||||
|
||||
auto& rwcDefaults = ReadWriteConcernDefaults::get(opCtx->getServiceContext());
|
||||
auto newDefaults = rwcDefaults.setConcerns(opCtx, rc, wc);
|
||||
auto newDefaults = rwcDefaults.setConcerns(
|
||||
opCtx, request().getDefaultReadConcern(), request().getDefaultWriteConcern());
|
||||
log() << "successfully set RWC defaults to " << newDefaults.toBSON();
|
||||
return newDefaults;
|
||||
}
|
||||
|
|
@ -83,7 +90,7 @@ public:
|
|||
}
|
||||
|
||||
void doCheckAuthorization(OperationContext*) const override {
|
||||
// TODO: add and use privilege action
|
||||
// TODO SERVER-45038: add and use privilege action
|
||||
}
|
||||
|
||||
NamespaceString ns() const override {
|
||||
|
|
@ -112,6 +119,10 @@ public:
|
|||
using InvocationBase::InvocationBase;
|
||||
|
||||
auto typedRun(OperationContext* opCtx) {
|
||||
assertNotStandaloneOrShardServer(opCtx, GetDefaultRWConcern::kCommandName);
|
||||
|
||||
// TODO SERVER-43720 Implement inMemory option.
|
||||
|
||||
auto& rwcDefaults = ReadWriteConcernDefaults::get(opCtx->getServiceContext());
|
||||
return rwcDefaults.getDefault(opCtx);
|
||||
}
|
||||
|
|
@ -122,7 +133,7 @@ public:
|
|||
}
|
||||
|
||||
void doCheckAuthorization(OperationContext*) const override {
|
||||
// TODO: add and use privilege action
|
||||
// TODO SERVER-45038: add and use privilege action
|
||||
}
|
||||
|
||||
NamespaceString ns() const override {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ imports:
|
|||
- "mongo/db/repl/read_concern_args.idl"
|
||||
- "mongo/db/write_concern_options.idl"
|
||||
|
||||
|
||||
commands:
|
||||
setDefaultRWConcern:
|
||||
description: "set the current read/write concern defaults (cluster-wide)"
|
||||
|
|
@ -52,3 +51,8 @@ commands:
|
|||
getDefaultRWConcern:
|
||||
description: "get the current read/write concern defaults being applied by this node"
|
||||
namespace: ignored
|
||||
fields:
|
||||
inMemory:
|
||||
type: bool
|
||||
description: "If true, return the locally cached read/write concern defaults"
|
||||
optional: true
|
||||
|
|
|
|||
|
|
@ -83,7 +83,12 @@ void ReadWriteConcernDefaults::_setDefault(RWConcernDefault&& rwc) {
|
|||
RWConcernDefault ReadWriteConcernDefaults::setConcerns(OperationContext* opCtx,
|
||||
const boost::optional<ReadConcern>& rc,
|
||||
const boost::optional<WriteConcern>& wc) {
|
||||
invariant(rc || wc);
|
||||
uassert(ErrorCodes::BadValue,
|
||||
str::stream() << "At least one of the \""
|
||||
<< RWConcernDefault::kDefaultReadConcernFieldName << "\" or \""
|
||||
<< RWConcernDefault::kDefaultWriteConcernFieldName
|
||||
<< "\" fields must be present",
|
||||
rc || wc);
|
||||
|
||||
if (rc) {
|
||||
checkSuitabilityAsDefault(*rc);
|
||||
|
|
@ -96,8 +101,12 @@ RWConcernDefault ReadWriteConcernDefaults::setConcerns(OperationContext* opCtx,
|
|||
auto epoch = LogicalClock::get(opCtx->getServiceContext())->getClusterTime().asTimestamp();
|
||||
|
||||
RWConcernDefault rwc;
|
||||
rwc.setDefaultReadConcern(rc);
|
||||
rwc.setDefaultWriteConcern(wc);
|
||||
if (rc && !rc->isEmpty()) {
|
||||
rwc.setDefaultReadConcern(rc);
|
||||
}
|
||||
if (wc && !wc->usedDefaultW) {
|
||||
rwc.setDefaultWriteConcern(wc);
|
||||
}
|
||||
rwc.setEpoch(epoch);
|
||||
rwc.setSetTime(now);
|
||||
rwc.setLocalSetTime(now);
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ public:
|
|||
* Interface when an admin has run the command to change the defaults.
|
||||
* At least one of the `rc` or `wc` params must be set.
|
||||
* Will generate and use a new epoch and setTime for the updated defaults, which are returned.
|
||||
* Validates the supplied read and write concerns can serve as defaults.
|
||||
*/
|
||||
RWConcernDefault setConcerns(OperationContext* opCtx,
|
||||
const boost::optional<ReadConcern>& rc,
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ BSONObj ReadConcernArgs::toBSON() const {
|
|||
return bob.obj();
|
||||
}
|
||||
|
||||
BSONObj ReadConcernArgs::toBSONInner() const {
|
||||
BSONObjBuilder bob;
|
||||
_appendInfoInner(&bob);
|
||||
return bob.obj();
|
||||
}
|
||||
|
||||
bool ReadConcernArgs::isEmpty() const {
|
||||
return !_afterClusterTime && !_opTime && !_atClusterTime && !_level;
|
||||
}
|
||||
|
|
@ -261,25 +267,27 @@ bool ReadConcernArgs::isSpeculativeMajority() const {
|
|||
_majorityReadMechanism == MajorityReadMechanism::kSpeculative;
|
||||
}
|
||||
|
||||
void ReadConcernArgs::appendInfo(BSONObjBuilder* builder) const {
|
||||
BSONObjBuilder rcBuilder(builder->subobjStart(kReadConcernFieldName));
|
||||
|
||||
void ReadConcernArgs::_appendInfoInner(BSONObjBuilder* builder) const {
|
||||
if (_level) {
|
||||
rcBuilder.append(kLevelFieldName, readConcernLevels::toString(_level.get()));
|
||||
builder->append(kLevelFieldName, readConcernLevels::toString(_level.get()));
|
||||
}
|
||||
|
||||
if (_opTime) {
|
||||
_opTime->append(&rcBuilder, kAfterOpTimeFieldName.toString());
|
||||
_opTime->append(builder, kAfterOpTimeFieldName.toString());
|
||||
}
|
||||
|
||||
if (_afterClusterTime) {
|
||||
rcBuilder.append(kAfterClusterTimeFieldName, _afterClusterTime->asTimestamp());
|
||||
builder->append(kAfterClusterTimeFieldName, _afterClusterTime->asTimestamp());
|
||||
}
|
||||
|
||||
if (_atClusterTime) {
|
||||
rcBuilder.append(kAtClusterTimeFieldName, _atClusterTime->asTimestamp());
|
||||
builder->append(kAtClusterTimeFieldName, _atClusterTime->asTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
void ReadConcernArgs::appendInfo(BSONObjBuilder* builder) const {
|
||||
BSONObjBuilder rcBuilder(builder->subobjStart(kReadConcernFieldName));
|
||||
_appendInfoInner(&rcBuilder);
|
||||
rcBuilder.done();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ public:
|
|||
bool isSpeculativeMajority() const;
|
||||
|
||||
/**
|
||||
* Appends level and afterOpTime.
|
||||
* Appends level, afterOpTime, and any other sub-fields in a 'readConcern' sub-object.
|
||||
*/
|
||||
void appendInfo(BSONObjBuilder* builder) const;
|
||||
|
||||
|
|
@ -165,9 +165,15 @@ public:
|
|||
|
||||
boost::optional<LogicalTime> getArgsAtClusterTime() const;
|
||||
BSONObj toBSON() const;
|
||||
BSONObj toBSONInner() const;
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Appends level, afterOpTime, and the other "inner" fields of the read concern args.
|
||||
*/
|
||||
void _appendInfoInner(BSONObjBuilder* builder) const;
|
||||
|
||||
/**
|
||||
* Read data after the OpTime of an operation on this replica set. Deprecated.
|
||||
* The only user is for read-after-optime calls using the config server optime.
|
||||
|
|
|
|||
|
|
@ -40,5 +40,5 @@ types:
|
|||
description: "An object representing a read concern."
|
||||
bson_serialization_type: object
|
||||
cpp_type: "mongo::repl::ReadConcernArgs"
|
||||
serializer: "mongo::repl::ReadConcernArgs::toBSON"
|
||||
serializer: "mongo::repl::ReadConcernArgs::toBSONInner"
|
||||
deserializer: "mongo::repl::ReadConcernArgs::fromBSONThrows"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ imports:
|
|||
structs:
|
||||
RWConcernDefault:
|
||||
description: "Represents a set of read/write concern defaults, and associated metadata"
|
||||
strict: false
|
||||
fields:
|
||||
defaultReadConcern:
|
||||
description: "The default read concern"
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ env.Library(
|
|||
'cluster_repl_set_get_status_cmd.cpp',
|
||||
'cluster_reset_error_cmd.cpp',
|
||||
'cluster_restart_catalog_command.cpp',
|
||||
'cluster_rwc_defaults_commands.cpp',
|
||||
'cluster_set_index_commit_quorum_cmd.cpp',
|
||||
'cluster_set_feature_compatibility_version_cmd.cpp',
|
||||
'cluster_set_free_monitoring.cpp' if get_option("enable-free-mon") == 'on' else [],
|
||||
|
|
@ -116,6 +117,7 @@ env.Library(
|
|||
'$BUILD_DIR/mongo/db/ftdc/ftdc_server',
|
||||
'$BUILD_DIR/mongo/db/shared_request_handling',
|
||||
'$BUILD_DIR/mongo/db/logical_session_cache_impl',
|
||||
'$BUILD_DIR/mongo/db/read_write_concern_defaults',
|
||||
'$BUILD_DIR/mongo/db/pipeline/aggregation',
|
||||
'$BUILD_DIR/mongo/db/query/command_request_response',
|
||||
'$BUILD_DIR/mongo/db/query/map_reduce_output_format',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* Copyright (C) 2019-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
|
||||
|
||||
#include "mongo/platform/basic.h"
|
||||
|
||||
#include "mongo/db/auth/authorization_session.h"
|
||||
#include "mongo/db/commands.h"
|
||||
#include "mongo/db/commands/rwc_defaults_commands_gen.h"
|
||||
#include "mongo/db/namespace_string.h"
|
||||
#include "mongo/db/read_write_concern_defaults.h"
|
||||
#include "mongo/db/rw_concern_default_gen.h"
|
||||
#include "mongo/s/cluster_commands_helpers.h"
|
||||
#include "mongo/s/grid.h"
|
||||
#include "mongo/util/log.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Implements the setDefaultRWConcern command on mongos. Inherits from BasicCommand because this
|
||||
* command forwards the user's request to the config server and does not need to parse it.
|
||||
*/
|
||||
class ClusterSetDefaultRWConcernCommand : public BasicCommand {
|
||||
public:
|
||||
ClusterSetDefaultRWConcernCommand() : BasicCommand("setDefaultRWConcern") {}
|
||||
|
||||
bool run(OperationContext* opCtx,
|
||||
const std::string& dbName,
|
||||
const BSONObj& cmdObj,
|
||||
BSONObjBuilder& result) override {
|
||||
auto configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
|
||||
auto cmdResponse = uassertStatusOK(configShard->runCommandWithFixedRetryAttempts(
|
||||
opCtx,
|
||||
ReadPreferenceSetting(ReadPreference::PrimaryOnly),
|
||||
NamespaceString::kAdminDb.toString(),
|
||||
CommandHelpers::appendMajorityWriteConcern(
|
||||
CommandHelpers::filterCommandRequestForPassthrough(cmdObj)),
|
||||
Shard::RetryPolicy::kNotIdempotent));
|
||||
|
||||
uassertStatusOK(cmdResponse.commandStatus);
|
||||
uassertStatusOK(cmdResponse.writeConcernStatus);
|
||||
|
||||
CommandHelpers::filterCommandReplyForPassthrough(cmdResponse.response, &result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool supportsWriteConcern(const BSONObj& cmd) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
Status checkAuthForOperation(OperationContext* opCtx,
|
||||
const std::string& dbname,
|
||||
const BSONObj& cmdObj) const override {
|
||||
// TODO SERVER-45038: add and use privilege action
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::string help() const override {
|
||||
return "Sets the default read or write concern for a cluster";
|
||||
}
|
||||
|
||||
bool adminOnly() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
|
||||
return AllowedOnSecondary::kNever;
|
||||
}
|
||||
} clusterSetDefaultRWConcernCommand;
|
||||
|
||||
/**
|
||||
* Implements the getDefaultRWConcern command on mongos.
|
||||
*/
|
||||
class ClusterGetDefaultRWConcernCommand final
|
||||
: public TypedCommand<ClusterGetDefaultRWConcernCommand> {
|
||||
public:
|
||||
using Request = GetDefaultRWConcern;
|
||||
using Response = RWConcernDefault;
|
||||
|
||||
class Invocation final : public InvocationBase {
|
||||
public:
|
||||
using InvocationBase::InvocationBase;
|
||||
|
||||
Response typedRun(OperationContext* opCtx) {
|
||||
// TODO SERVER-43720 Implement inMemory option.
|
||||
|
||||
GetDefaultRWConcern configsvrRequest;
|
||||
configsvrRequest.setDbName(request().getDbName());
|
||||
|
||||
auto configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
|
||||
auto cmdResponse = uassertStatusOK(configShard->runCommandWithFixedRetryAttempts(
|
||||
opCtx,
|
||||
ReadPreferenceSetting(ReadPreference::PrimaryOnly),
|
||||
NamespaceString::kAdminDb.toString(),
|
||||
applyReadWriteConcern(opCtx, this, configsvrRequest.toBSON({})),
|
||||
Shard::RetryPolicy::kIdempotent));
|
||||
|
||||
uassertStatusOK(cmdResponse.commandStatus);
|
||||
|
||||
return Response::parse(IDLParserErrorContext("ClusterGetDefaultRWConcernResponse"),
|
||||
cmdResponse.response);
|
||||
}
|
||||
|
||||
private:
|
||||
bool supportsWriteConcern() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void doCheckAuthorization(OperationContext*) const override {
|
||||
// TODO SERVER-45038: add and use privilege action
|
||||
}
|
||||
|
||||
NamespaceString ns() const override {
|
||||
return NamespaceString(request().getDbName(), "");
|
||||
}
|
||||
};
|
||||
|
||||
std::string help() const override {
|
||||
return "Gets the default read or write concern for a cluster";
|
||||
}
|
||||
|
||||
bool adminOnly() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
|
||||
return AllowedOnSecondary::kNever;
|
||||
}
|
||||
} clusterGetDefaultRWConcernCommand;
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo
|
||||
|
|
@ -284,7 +284,7 @@ assert = (function() {
|
|||
|
||||
if (count != arr.length) {
|
||||
doassert(_buildAssertionMessage(
|
||||
msg, "None of values from " + tojson(arr) + " was in " + tojson(result)));
|
||||
msg, "Not all of the values from " + tojson(arr) + " were in " + tojson(result)));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue