summaryrefslogtreecommitdiff
path: root/src/mongo/db/repl/replication_info.cpp
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2021-02-10 14:30:01 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-02-10 15:26:00 +0000
commit87bcb24f50b65e08eb0d711edfb59b61ef3c1f91 (patch)
tree15385fc4b0c36072c95be9bb1fcb3e42b925c269 /src/mongo/db/repl/replication_info.cpp
parent7d5cf026105db1693724db155b4f409b267ab352 (diff)
downloadmongo-87bcb24f50b65e08eb0d711edfb59b61ef3c1f91.tar.gz
SERVER-53150 Specify input/output to hello command
Diffstat (limited to 'src/mongo/db/repl/replication_info.cpp')
-rw-r--r--src/mongo/db/repl/replication_info.cpp217
1 files changed, 97 insertions, 120 deletions
diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp
index 7c051a60ebd..ba7c3131844 100644
--- a/src/mongo/db/repl/replication_info.cpp
+++ b/src/mongo/db/repl/replication_info.cpp
@@ -39,6 +39,7 @@
#include "mongo/client/dbclient_connection.h"
#include "mongo/db/client.h"
#include "mongo/db/commands/server_status.h"
+#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/curop.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbhelpers.h"
@@ -50,6 +51,7 @@
#include "mongo/db/ops/write_ops.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/repl/hello_auth.h"
+#include "mongo/db/repl/hello_gen.h"
#include "mongo/db/repl/hello_response.h"
#include "mongo/db/repl/primary_only_service.h"
#include "mongo/db/repl/replication_auth.h"
@@ -101,7 +103,7 @@ TopologyVersion appendReplicationInfo(OperationContext* opCtx,
bool appendReplicationProcess,
bool useLegacyResponseFields,
boost::optional<TopologyVersion> clientTopologyVersion,
- boost::optional<long long> maxAwaitTimeMS) {
+ boost::optional<std::int64_t> maxAwaitTimeMS) {
TopologyVersion topologyVersion;
ReplicationCoordinator* replCoord = ReplicationCoordinator::get(opCtx);
if (replCoord->getSettings().usingReplSets()) {
@@ -237,11 +239,14 @@ public:
}
} oplogInfoServerStatus;
+const std::string kAutomationServiceDescriptorFieldName =
+ HelloCommandReply::kAutomationServiceDescriptorFieldName.toString();
+
class CmdHello : public BasicCommandWithReplyBuilderInterface {
public:
CmdHello() : CmdHello(kHelloString, {}) {}
- const std::set<std::string>& apiVersions() const {
+ const std::set<std::string>& apiVersions() const final {
return kApiVersions1;
}
@@ -271,13 +276,15 @@ public:
const BSONObj& cmdObj,
rpc::ReplyBuilderInterface* replyBuilder) final {
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
+ const bool apiStrict = APIParameters::get(opCtx).getAPIStrict().value_or(false);
+ auto cmd = HelloCommand::parse({"hello", apiStrict}, cmdObj);
waitInHello.pauseWhileSet(opCtx);
/* currently request to arbiter is (somewhat arbitrarily) an ismaster request that is not
authenticated.
*/
- if (cmdObj["forShell"].trueValue()) {
+ if (cmd.getForShell()) {
LastError::get(opCtx->getClient()).disable();
}
@@ -285,8 +292,7 @@ public:
transport::Session::TagMask sessionTagsToUnset = 0;
// Tag connections to avoid closing them on stepdown.
- auto hangUpElement = cmdObj["hangUpOnStepDown"];
- if (!hangUpElement.eoo() && !hangUpElement.trueValue()) {
+ if (!cmd.getHangUpOnStepDown()) {
sessionTagsToSet |= transport::Session::kKeepOpen;
}
@@ -299,51 +305,18 @@ public:
// Parse the optional 'internalClient' field. This is provided by incoming connections from
// mongod and mongos.
- auto internalClientElement = cmdObj["internalClient"];
- if (internalClientElement) {
+ if (auto internalClient = cmd.getInternalClient()) {
sessionTagsToSet |= transport::Session::kInternalClient;
sessionTagsToUnset |= transport::Session::kExternalClientKeepOpen;
- uassert(ErrorCodes::TypeMismatch,
- str::stream() << "'internalClient' must be of type Object, but was of type "
- << typeName(internalClientElement.type()),
- internalClientElement.type() == BSONType::Object);
-
- bool foundMaxWireVersion = false;
- for (auto&& elem : internalClientElement.Obj()) {
- auto fieldName = elem.fieldNameStringData();
- if (fieldName == "minWireVersion") {
- // We do not currently use 'internalClient.minWireVersion'.
- continue;
- } else if (fieldName == "maxWireVersion") {
- foundMaxWireVersion = true;
-
- uassert(ErrorCodes::TypeMismatch,
- str::stream() << "'maxWireVersion' field of 'internalClient' must be "
- "of type int, but was of type "
- << typeName(elem.type()),
- elem.type() == BSONType::NumberInt);
-
- // All incoming connections from mongod/mongos of earlier versions should be
- // closed if the featureCompatibilityVersion is bumped to 3.6.
- if (elem.numberInt() >=
- WireSpec::instance().get()->incomingInternalClient.maxWireVersion) {
- sessionTagsToSet |=
- transport::Session::kLatestVersionInternalClientKeepOpen;
- } else {
- sessionTagsToUnset |=
- transport::Session::kLatestVersionInternalClientKeepOpen;
- }
- } else {
- uasserted(ErrorCodes::BadValue,
- str::stream() << "Unrecognized field of 'internalClient': '"
- << fieldName << "'");
- }
+ // All incoming connections from mongod/mongos of earlier versions should be
+ // closed if the featureCompatibilityVersion is bumped to 3.6.
+ if (internalClient->getMaxWireVersion() >=
+ WireSpec::instance().get()->incomingInternalClient.maxWireVersion) {
+ sessionTagsToSet |= transport::Session::kLatestVersionInternalClientKeepOpen;
+ } else {
+ sessionTagsToUnset |= transport::Session::kLatestVersionInternalClientKeepOpen;
}
-
- uassert(ErrorCodes::BadValue,
- "Missing required field 'maxWireVersion' of 'internalClient'",
- foundMaxWireVersion);
} else {
sessionTagsToUnset |= (transport::Session::kInternalClient |
transport::Session::kLatestVersionInternalClientKeepOpen);
@@ -369,53 +342,36 @@ public:
// If a client is following the awaitable hello protocol, maxAwaitTimeMS should be
// present if and only if topologyVersion is present in the request.
- auto topologyVersionElement = cmdObj["topologyVersion"];
- auto maxAwaitTimeMSField = cmdObj["maxAwaitTimeMS"];
+ auto clientTopologyVersion = cmd.getTopologyVersion();
+ auto maxAwaitTimeMS = cmd.getMaxAwaitTimeMS();
auto curOp = CurOp::get(opCtx);
-
- // The awaitable field is optional, but if it exists, it requires both other fields.
- // Note that the awaitable field only exists to make filtering out these operations
- // via currentOp queries easier and is not otherwise explicitly used.
- {
- bool awaitable = true;
- Status status = bsonExtractBooleanField(cmdObj, "awaitable", &awaitable);
- if (status.isOK() && (!topologyVersionElement || !maxAwaitTimeMSField)) {
- uassert(5135800,
- "A request marked awaitable must contain both 'topologyVersion' and "
- "'maxAwaitTimeMS'",
- !awaitable);
- }
- }
-
- boost::optional<TopologyVersion> clientTopologyVersion;
- boost::optional<long long> maxAwaitTimeMS;
boost::optional<ScopeGuard<std::function<void()>>> timerGuard;
- if (topologyVersionElement && maxAwaitTimeMSField) {
- clientTopologyVersion = TopologyVersion::parse(IDLParserErrorContext("TopologyVersion"),
- topologyVersionElement.Obj());
+ if (clientTopologyVersion && maxAwaitTimeMS) {
uassert(31372,
"topologyVersion must have a non-negative counter",
clientTopologyVersion->getCounter() >= 0);
- {
- long long parsedMaxAwaitTimeMS;
- uassertStatusOK(
- bsonExtractIntegerField(cmdObj, "maxAwaitTimeMS", &parsedMaxAwaitTimeMS));
- maxAwaitTimeMS = parsedMaxAwaitTimeMS;
- }
-
- uassert(31373, "maxAwaitTimeMS must be a non-negative integer", *maxAwaitTimeMS >= 0);
-
- LOGV2_DEBUG(23904, 3, "Using maxAwaitTimeMS for awaitable hello protocol.");
+ LOGV2_DEBUG(23904,
+ 3,
+ "Using maxAwaitTimeMS for awaitable hello protocol",
+ "maxAwaitTimeMS"_attr = maxAwaitTimeMS.get());
curOp->pauseTimer();
timerGuard.emplace([curOp]() { curOp->resumeTimer(); });
} else {
+ // The awaitable field is optional, but if it exists, it requires both other fields.
+ // Note that the awaitable field only exists to make filtering out these operations
+ // via currentOp queries easier and is not otherwise explicitly used.
+ uassert(5135800,
+ "A request marked awaitable must contain both 'topologyVersion' and "
+ "'maxAwaitTimeMS'",
+ !cmd.getAwaitable() || !cmd.getAwaitable());
+
uassert(31368,
- (topologyVersionElement
+ (clientTopologyVersion
? "A request with a 'topologyVersion' must include 'maxAwaitTimeMS'"
: "A request with 'maxAwaitTimeMS' must include a 'topologyVersion'"),
- !topologyVersionElement && !maxAwaitTimeMSField);
+ !clientTopologyVersion && !maxAwaitTimeMS);
}
auto result = replyBuilder->getBodyBuilder();
@@ -424,21 +380,18 @@ public:
// handshake for an incoming connection if the client supports the hello command. Clients
// that specify 'helloOk' do not rely on "not master" error message parsing, which means
// that we can safely return "not primary" error messages instead.
- bool helloOk = client->supportsHello();
- Status status = bsonExtractBooleanField(cmdObj, "helloOk", &helloOk);
- if (status.isOK()) {
+ if (auto helloOk = cmd.getHelloOk()) {
// If the hello request contains a "helloOk" field, set _supportsHello on the Client
// to the value.
- client->setSupportsHello(helloOk);
+ client->setSupportsHello(*helloOk);
// Attach helloOk: true to the response so that the client knows the server supports
// the hello command.
- result.append("helloOk", true);
- } else if (status.code() != ErrorCodes::NoSuchKey) {
- uassertStatusOK(status);
+ result.append(HelloCommandReply::kHelloOkFieldName, true);
}
if (MONGO_unlikely(appendHelloOkToHelloResponse.shouldFail())) {
- result.append("clientSupportsHello", client->supportsHello());
+ result.append(HelloCommandReply::kClientSupportsHelloFieldName,
+ client->supportsHello());
}
auto currentTopologyVersion = appendReplicationInfo(
@@ -447,35 +400,44 @@ public:
timerGuard.reset(); // Resume curOp timer.
if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) {
- const int configServerModeNumber = 2;
- result.append("configsvr", configServerModeNumber);
+ constexpr int kConfigServerModeNumber = 2;
+ result.append(HelloCommandReply::kConfigsvrFieldName, kConfigServerModeNumber);
}
- result.appendNumber("maxBsonObjectSize", BSONObjMaxUserSize);
- result.appendNumber("maxMessageSizeBytes", MaxMessageSizeBytes);
- result.appendNumber("maxWriteBatchSize", write_ops::kMaxWriteBatchSize);
- result.appendDate("localTime", jsTime());
- result.append("logicalSessionTimeoutMinutes", localLogicalSessionTimeoutMinutes);
- result.appendNumber("connectionId", opCtx->getClient()->getConnectionId());
-
- if (auto wireSpec = WireSpec::instance().get(); internalClientElement) {
- result.append("minWireVersion", wireSpec->incomingInternalClient.minWireVersion);
- result.append("maxWireVersion", wireSpec->incomingInternalClient.maxWireVersion);
+ result.appendNumber(HelloCommandReply::kMaxBsonObjectSizeFieldName, BSONObjMaxUserSize);
+ result.appendNumber(HelloCommandReply::kMaxMessageSizeBytesFieldName, MaxMessageSizeBytes);
+ result.appendNumber(HelloCommandReply::kMaxWriteBatchSizeFieldName,
+ write_ops::kMaxWriteBatchSize);
+ result.appendDate(HelloCommandReply::kLocalTimeFieldName, jsTime());
+ result.append(HelloCommandReply::kLogicalSessionTimeoutMinutesFieldName,
+ localLogicalSessionTimeoutMinutes);
+ result.appendNumber(HelloCommandReply::kConnectionIdFieldName,
+ opCtx->getClient()->getConnectionId());
+
+
+ if (auto wireSpec = WireSpec::instance().get(); cmd.getInternalClient()) {
+ result.append(HelloCommandReply::kMinWireVersionFieldName,
+ wireSpec->incomingInternalClient.minWireVersion);
+ result.append(HelloCommandReply::kMaxWireVersionFieldName,
+ wireSpec->incomingInternalClient.maxWireVersion);
} else {
- result.append("minWireVersion", wireSpec->incomingExternalClient.minWireVersion);
- result.append("maxWireVersion", wireSpec->incomingExternalClient.maxWireVersion);
+ result.append(HelloCommandReply::kMinWireVersionFieldName,
+ wireSpec->incomingExternalClient.minWireVersion);
+ result.append(HelloCommandReply::kMaxWireVersionFieldName,
+ wireSpec->incomingExternalClient.maxWireVersion);
}
- result.append("readOnly", storageGlobalParams.readOnly);
+ result.append(HelloCommandReply::kReadOnlyFieldName, storageGlobalParams.readOnly);
const auto& params = ServerParameterSet::getGlobal()->getMap();
- if (auto iter = params.find("automationServiceDescriptor");
- iter != params.end() && iter->second)
- iter->second->append(opCtx, result, "automationServiceDescriptor");
+ if (auto iter = params.find(kAutomationServiceDescriptorFieldName);
+ iter != params.end() && iter->second) {
+ iter->second->append(opCtx, result, kAutomationServiceDescriptorFieldName);
+ }
if (opCtx->getClient()->session()) {
MessageCompressorManager::forSession(opCtx->getClient()->session())
- .serverNegotiate(cmdObj, &result);
+ .serverNegotiate(cmd.getCompression(), &result);
}
if (opCtx->isExhaust()) {
@@ -483,7 +445,7 @@ public:
uassert(51756,
"An isMaster or hello request with exhaust must specify 'maxAwaitTimeMS'",
- maxAwaitTimeMSField);
+ maxAwaitTimeMS);
invariant(clientTopologyVersion);
InExhaustHello::get(opCtx->getClient()->session().get())
@@ -495,30 +457,45 @@ public:
// command parameters should be reused as the next BSONObj command parameters.
replyBuilder->setNextInvocation(boost::none);
} else {
- BSONObjBuilder nextInvocationBuilder;
- for (auto&& elt : cmdObj) {
- if (elt.fieldNameStringData() == "topologyVersion"_sd) {
- BSONObjBuilder topologyVersionBuilder(
- nextInvocationBuilder.subobjStart("topologyVersion"));
- currentTopologyVersion.serialize(&topologyVersionBuilder);
+ BSONObjBuilder niBuilder;
+ for (const auto& elem : cmdObj) {
+ if (elem.fieldNameStringData() == HelloCommand::kTopologyVersionFieldName) {
+ BSONObjBuilder tvBuilder(
+ niBuilder.subobjStart(HelloCommand::kTopologyVersionFieldName));
+ currentTopologyVersion.serialize(&tvBuilder);
} else {
- nextInvocationBuilder.append(elt);
+ niBuilder.append(elem);
}
}
- replyBuilder->setNextInvocation(nextInvocationBuilder.obj());
+ replyBuilder->setNextInvocation(niBuilder.obj());
}
}
- handleHelloAuth(opCtx, cmdObj, &result);
+ handleHelloAuth(opCtx, cmd, &result);
+ if (getTestCommandsEnabled()) {
+ validateResult(&result);
+ }
return true;
}
+ void validateResult(BSONObjBuilder* result) {
+ auto ret = result->asTempObj();
+ if (ret[ErrorReply::kErrmsgFieldName].eoo()) {
+ // Nominal success case, parse the object as-is.
+ HelloCommandReply::parse({"hello.reply"}, ret);
+ } else {
+ // Something went wrong, still try to parse, but accept a few ignorable fields.
+ StringDataSet ignorable({ErrorReply::kCodeFieldName, ErrorReply::kErrmsgFieldName});
+ HelloCommandReply::parse({"hello.reply"}, ret.removeFields(ignorable));
+ }
+ }
+
protected:
CmdHello(const StringData cmdName, const std::initializer_list<StringData>& alias)
: BasicCommandWithReplyBuilderInterface(cmdName, alias) {}
- virtual bool useLegacyResponseFields() {
+ virtual bool useLegacyResponseFields() const {
return false;
}
@@ -528,7 +505,7 @@ class CmdIsMaster : public CmdHello {
public:
CmdIsMaster() : CmdHello(kCamelCaseIsMasterString, {kLowerCaseIsMasterString}) {}
- std::string help() const override {
+ std::string help() const final {
return "Check if this server is primary for a replica set\n"
"{ isMaster : 1 }";
}
@@ -537,7 +514,7 @@ protected:
// Parse the command name, which should be one of the following: hello, isMaster, or
// ismaster. If the command is "hello", we must attach an "isWritablePrimary" response field
// instead of "ismaster" and "secondaryDelaySecs" response field instead of "slaveDelay".
- bool useLegacyResponseFields() override {
+ bool useLegacyResponseFields() const final {
return true;
}