diff options
Diffstat (limited to 'src/mongo/db/repl/hello_response.cpp')
-rw-r--r-- | src/mongo/db/repl/hello_response.cpp | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/src/mongo/db/repl/hello_response.cpp b/src/mongo/db/repl/hello_response.cpp new file mode 100644 index 00000000000..89ae3426073 --- /dev/null +++ b/src/mongo/db/repl/hello_response.cpp @@ -0,0 +1,605 @@ +/** + * Copyright (C) 2018-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_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kReplication + +#include "mongo/platform/basic.h" + +#include "mongo/db/repl/hello_response.h" + +#include "mongo/base/status.h" +#include "mongo/bson/oid.h" +#include "mongo/bson/util/bson_extract.h" +#include "mongo/db/jsobj.h" +#include "mongo/util/str.h" + +namespace mongo { +namespace repl { +namespace { + +const std::string kIsMasterFieldName = "ismaster"; +const std::string kSecondaryFieldName = "secondary"; +const std::string kSetNameFieldName = "setName"; +const std::string kSetVersionFieldName = "setVersion"; +const std::string kTopologyVersionFieldName = "topologyVersion"; +const std::string kHostsFieldName = "hosts"; +const std::string kPassivesFieldName = "passives"; +const std::string kArbitersFieldName = "arbiters"; +const std::string kPrimaryFieldName = "primary"; +const std::string kArbiterOnlyFieldName = "arbiterOnly"; +const std::string kPassiveFieldName = "passive"; +const std::string kHiddenFieldName = "hidden"; +const std::string kBuildIndexesFieldName = "buildIndexes"; +const std::string kSlaveDelayFieldName = "slaveDelay"; +const std::string kTagsFieldName = "tags"; +const std::string kMeFieldName = "me"; +const std::string kElectionIdFieldName = "electionId"; +const std::string kLastWriteOpTimeFieldName = "opTime"; +const std::string kLastWriteDateFieldName = "lastWriteDate"; +const std::string kLastMajorityWriteOpTimeFieldName = "majorityOpTime"; +const std::string kLastMajorityWriteDateFieldName = "majorityWriteDate"; +const std::string kLastWriteFieldName = "lastWrite"; +const std::string kIsWritablePrimaryFieldName = "isWritablePrimary"; +const std::string kSecondaryDelaySecsFieldName = "secondaryDelaySecs"; + +// field name constants that don't directly correspond to member variables +const std::string kInfoFieldName = "info"; +const std::string kIsReplicaSetFieldName = "isreplicaset"; +const std::string kErrmsgFieldName = "errmsg"; +const std::string kCodeFieldName = "code"; + +} // namespace + +HelloResponse::HelloResponse() + : _isWritablePrimary(false), + _isWritablePrimarySet(false), + _secondary(false), + _isSecondarySet(false), + _setNameSet(false), + _setVersion(0), + _setVersionSet(false), + _hostsSet(false), + _passivesSet(false), + _arbitersSet(false), + _primarySet(false), + _arbiterOnly(false), + _arbiterOnlySet(false), + _passive(false), + _passiveSet(false), + _hidden(false), + _hiddenSet(false), + _buildIndexes(true), + _buildIndexesSet(false), + _secondaryDelaySecs(0), + _secondaryDelaySecsSet(false), + _tagsSet(false), + _meSet(false), + _electionId(OID()), + _configSet(true), + _shutdownInProgress(false) {} + +void HelloResponse::addToBSON(BSONObjBuilder* builder, bool useLegacyResponseFields) const { + if (_topologyVersion) { + BSONObjBuilder topologyVersionBuilder(builder->subobjStart(kTopologyVersionFieldName)); + _topologyVersion->serialize(&topologyVersionBuilder); + } + + if (_hostsSet) { + std::vector<std::string> hosts; + for (size_t i = 0; i < _hosts.size(); ++i) { + hosts.push_back(_hosts[i].toString()); + } + builder->append(kHostsFieldName, hosts); + } + if (_passivesSet) { + std::vector<std::string> passives; + for (size_t i = 0; i < _passives.size(); ++i) { + passives.push_back(_passives[i].toString()); + } + builder->append(kPassivesFieldName, passives); + } + if (_arbitersSet) { + std::vector<std::string> arbiters; + for (size_t i = 0; i < _arbiters.size(); ++i) { + arbiters.push_back(_arbiters[i].toString()); + } + builder->append(kArbitersFieldName, arbiters); + } + + if (_setNameSet) { + builder->append(kSetNameFieldName, _setName); + } + + if (_shutdownInProgress) { + builder->append(kCodeFieldName, ErrorCodes::ShutdownInProgress); + builder->append(kErrmsgFieldName, "replication shutdown in progress"); + return; + } + + if (!_configSet) { + if (useLegacyResponseFields) { + builder->append(kIsMasterFieldName, false); + } else { + builder->append(kIsWritablePrimaryFieldName, false); + } + builder->append(kSecondaryFieldName, false); + builder->append(kInfoFieldName, "Does not have a valid replica set config"); + builder->append(kIsReplicaSetFieldName, true); + return; + } + + invariant(_setVersionSet); + builder->append(kSetVersionFieldName, static_cast<int>(_setVersion)); + invariant(_isWritablePrimarySet); + if (useLegacyResponseFields) { + builder->append(kIsMasterFieldName, _isWritablePrimary); + } else { + builder->append(kIsWritablePrimaryFieldName, _isWritablePrimary); + } + invariant(_isSecondarySet); + builder->append(kSecondaryFieldName, _secondary); + + if (_primarySet) + builder->append(kPrimaryFieldName, _primary.toString()); + if (_arbiterOnlySet) + builder->append(kArbiterOnlyFieldName, _arbiterOnly); + if (_passiveSet) + builder->append(kPassiveFieldName, _passive); + if (_hiddenSet) + builder->append(kHiddenFieldName, _hidden); + if (_buildIndexesSet) + builder->append(kBuildIndexesFieldName, _buildIndexes); + if (_secondaryDelaySecsSet) { + if (useLegacyResponseFields) { + builder->appendIntOrLL(kSlaveDelayFieldName, + durationCount<Seconds>(_secondaryDelaySecs)); + } else { + builder->appendIntOrLL(kSecondaryDelaySecsFieldName, + durationCount<Seconds>(_secondaryDelaySecs)); + } + } + if (_tagsSet) { + BSONObjBuilder tags(builder->subobjStart(kTagsFieldName)); + for (stdx::unordered_map<std::string, std::string>::const_iterator it = _tags.begin(); + it != _tags.end(); + ++it) { + tags.append(it->first, it->second); + } + } + invariant(_meSet); + builder->append(kMeFieldName, _me.toString()); + if (_electionId.isSet()) + builder->append(kElectionIdFieldName, _electionId); + if (_lastWrite || _lastMajorityWrite) { + BSONObjBuilder lastWrite(builder->subobjStart(kLastWriteFieldName)); + if (_lastWrite) { + lastWrite.append(kLastWriteOpTimeFieldName, _lastWrite->opTime.toBSON()); + lastWrite.appendTimeT(kLastWriteDateFieldName, _lastWrite->value); + } + if (_lastMajorityWrite) { + lastWrite.append(kLastMajorityWriteOpTimeFieldName, + _lastMajorityWrite->opTime.toBSON()); + lastWrite.appendTimeT(kLastMajorityWriteDateFieldName, _lastMajorityWrite->value); + } + } +} + +BSONObj HelloResponse::toBSON(bool useLegacyResponseFields) const { + BSONObjBuilder builder; + addToBSON(&builder, useLegacyResponseFields); + return builder.obj(); +} + +Status HelloResponse::initialize(const BSONObj& doc) { + Status status = bsonExtractBooleanField(doc, kIsMasterFieldName, &_isWritablePrimary); + if (!status.isOK()) { + return status; + } + _isWritablePrimarySet = true; + status = bsonExtractBooleanField(doc, kSecondaryFieldName, &_secondary); + if (!status.isOK()) { + return status; + } + _isSecondarySet = true; + if (doc.hasField(kInfoFieldName)) { + if (_isWritablePrimary || _secondary || !doc.hasField(kIsReplicaSetFieldName) || + !doc[kIsReplicaSetFieldName].booleanSafe()) { + return Status(ErrorCodes::FailedToParse, + str::stream() << "Expected presence of \"" << kInfoFieldName + << "\" field to indicate no valid config loaded, but other " + "fields weren't as we expected"); + } + _configSet = false; + return Status::OK(); + } else { + if (doc.hasField(kIsReplicaSetFieldName)) { + return Status(ErrorCodes::FailedToParse, + str::stream() << "Found \"" << kIsReplicaSetFieldName + << "\" field which should indicate that no valid config " + "is loaded, but we didn't also have an \"" + << kInfoFieldName << "\" field as we expected"); + } + } + + status = bsonExtractStringField(doc, kSetNameFieldName, &_setName); + if (!status.isOK()) { + return status; + } + _setNameSet = true; + status = bsonExtractIntegerField(doc, kSetVersionFieldName, &_setVersion); + if (!status.isOK()) { + return status; + } + _setVersionSet = true; + + if (doc.hasField(kHostsFieldName)) { + BSONElement hostsElement; + status = bsonExtractTypedField(doc, kHostsFieldName, Array, &hostsElement); + if (!status.isOK()) { + return status; + } + for (BSONObjIterator it(hostsElement.Obj()); it.more();) { + BSONElement hostElement = it.next(); + if (hostElement.type() != String) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kHostsFieldName + << "\" array of hello response must be of type " + << typeName(String) << " but found type " + << typeName(hostElement.type())); + } + _hosts.push_back(HostAndPort(hostElement.String())); + } + _hostsSet = true; + } + + if (doc.hasField(kPassivesFieldName)) { + BSONElement passivesElement; + status = bsonExtractTypedField(doc, kPassivesFieldName, Array, &passivesElement); + if (!status.isOK()) { + return status; + } + for (BSONObjIterator it(passivesElement.Obj()); it.more();) { + BSONElement passiveElement = it.next(); + if (passiveElement.type() != String) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kPassivesFieldName + << "\" array of hello response must be of type " + << typeName(String) << " but found type " + << typeName(passiveElement.type())); + } + _passives.push_back(HostAndPort(passiveElement.String())); + } + _passivesSet = true; + } + + if (doc.hasField(kArbitersFieldName)) { + BSONElement arbitersElement; + status = bsonExtractTypedField(doc, kArbitersFieldName, Array, &arbitersElement); + if (!status.isOK()) { + return status; + } + for (BSONObjIterator it(arbitersElement.Obj()); it.more();) { + BSONElement arbiterElement = it.next(); + if (arbiterElement.type() != String) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kArbitersFieldName + << "\" array of hello response must be of type " + << typeName(String) << " but found type " + << typeName(arbiterElement.type())); + } + _arbiters.push_back(HostAndPort(arbiterElement.String())); + } + _arbitersSet = true; + } + + if (doc.hasField(kPrimaryFieldName)) { + std::string primaryString; + status = bsonExtractStringField(doc, kPrimaryFieldName, &primaryString); + if (!status.isOK()) { + return status; + } + _primary = HostAndPort(primaryString); + _primarySet = true; + } + + if (doc.hasField(kArbiterOnlyFieldName)) { + status = bsonExtractBooleanField(doc, kArbiterOnlyFieldName, &_arbiterOnly); + if (!status.isOK()) { + return status; + } + _arbiterOnlySet = true; + } + + if (doc.hasField(kPassiveFieldName)) { + status = bsonExtractBooleanField(doc, kPassiveFieldName, &_passive); + if (!status.isOK()) { + return status; + } + _passiveSet = true; + } + + if (doc.hasField(kHiddenFieldName)) { + status = bsonExtractBooleanField(doc, kHiddenFieldName, &_hidden); + if (!status.isOK()) { + return status; + } + _hiddenSet = true; + } + + if (doc.hasField(kBuildIndexesFieldName)) { + status = bsonExtractBooleanField(doc, kBuildIndexesFieldName, &_buildIndexes); + if (!status.isOK()) { + return status; + } + _buildIndexesSet = true; + } + + if (doc.hasField(kSlaveDelayFieldName)) { + long long secondaryDelaySecs; + status = bsonExtractIntegerField(doc, kSlaveDelayFieldName, &secondaryDelaySecs); + if (!status.isOK()) { + return status; + } + _secondaryDelaySecsSet = true; + _secondaryDelaySecs = Seconds(secondaryDelaySecs); + } + + if (doc.hasField(kTagsFieldName)) { + BSONElement tagsElement; + status = bsonExtractTypedField(doc, kTagsFieldName, Object, &tagsElement); + if (!status.isOK()) { + return status; + } + for (BSONObjIterator it(tagsElement.Obj()); it.more();) { + BSONElement tagElement = it.next(); + if (tagElement.type() != String) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kTagsFieldName + << "\" obj " + "of hello response must be of type " + << typeName(String) << " but found type " + << typeName(tagsElement.type())); + } + _tags[tagElement.fieldNameStringData().toString()] = tagElement.String(); + } + _tagsSet = true; + } + + if (doc.hasField(kElectionIdFieldName)) { + BSONElement electionIdElem; + status = bsonExtractTypedField(doc, kElectionIdFieldName, jstOID, &electionIdElem); + if (!status.isOK()) { + return status; + } + _electionId = electionIdElem.OID(); + } + + if (doc.hasField(kLastWriteFieldName)) { + BSONElement lastWriteElement; + status = bsonExtractTypedField(doc, kLastWriteFieldName, Object, &lastWriteElement); + if (!status.isOK()) { + return status; + } + BSONObj lastWriteObj = lastWriteElement.Obj(); + bool lastWriteOpTimeSet = false; + bool lastWriteDateSet = false; + if (auto lastWriteOpTimeElement = lastWriteObj[kLastWriteOpTimeFieldName]) { + if (lastWriteOpTimeElement.type() != Object) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kLastWriteOpTimeFieldName + << "\" obj " + "of hello response must be of type " + << typeName(Object) << " but found type " + << typeName(lastWriteOpTimeElement.type())); + } + auto lastWriteOpTime = OpTime::parseFromOplogEntry(lastWriteOpTimeElement.Obj()); + if (!lastWriteOpTime.isOK()) { + return lastWriteOpTime.getStatus(); + } + if (_lastWrite) { + _lastWrite->opTime = lastWriteOpTime.getValue(); + } else { + _lastWrite = OpTimeWith<time_t>(0, lastWriteOpTime.getValue()); + } + lastWriteOpTimeSet = true; + } + if (auto lastWriteDateElement = lastWriteObj[kLastWriteDateFieldName]) { + if (lastWriteDateElement.type() != Date) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kLastWriteDateFieldName + << "\" obj " + "of hello response must be of type " + << typeName(Date) << " but found type " + << typeName(lastWriteDateElement.type())); + } + if (_lastWrite) { + _lastWrite->value = lastWriteDateElement.Date().toTimeT(); + } else { + _lastWrite = OpTimeWith<time_t>(lastWriteDateElement.Date().toTimeT(), OpTime()); + } + lastWriteDateSet = true; + } + invariant(lastWriteOpTimeSet == lastWriteDateSet); + + bool lastMajorityWriteOpTimeSet = false; + bool lastMajorityWriteDateSet = false; + if (auto lastMajorityWriteOpTimeElement = lastWriteObj[kLastMajorityWriteOpTimeFieldName]) { + if (lastMajorityWriteOpTimeElement.type() != Object) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kLastMajorityWriteOpTimeFieldName + << "\" obj " + "of hello response must be of type " + << typeName(Object) << " but found type " + << typeName(lastMajorityWriteOpTimeElement.type())); + } + auto lastMajorityWriteOpTime = + OpTime::parseFromOplogEntry(lastMajorityWriteOpTimeElement.Obj()); + if (!lastMajorityWriteOpTime.isOK()) { + return lastMajorityWriteOpTime.getStatus(); + } + if (_lastMajorityWrite) { + _lastMajorityWrite->opTime = lastMajorityWriteOpTime.getValue(); + } else { + _lastMajorityWrite = OpTimeWith<time_t>(0, lastMajorityWriteOpTime.getValue()); + } + lastMajorityWriteOpTimeSet = true; + } + if (auto lastMajorityWriteDateElement = lastWriteObj[kLastMajorityWriteDateFieldName]) { + if (lastMajorityWriteDateElement.type() != Date) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Elements in \"" << kLastMajorityWriteDateFieldName + << "\" obj " + "of hello response must be of type " + << typeName(Date) << " but found type " + << typeName(lastMajorityWriteDateElement.type())); + } + if (_lastMajorityWrite) { + _lastMajorityWrite->value = lastMajorityWriteDateElement.Date().toTimeT(); + } else { + _lastMajorityWrite = + OpTimeWith<time_t>(lastMajorityWriteDateElement.Date().toTimeT(), OpTime()); + } + lastMajorityWriteDateSet = true; + } + invariant(lastMajorityWriteOpTimeSet == lastMajorityWriteDateSet); + } + + std::string meString; + status = bsonExtractStringField(doc, kMeFieldName, &meString); + if (!status.isOK()) { + return status; + } + _me = HostAndPort(meString); + _meSet = true; + + return Status::OK(); +} + +void HelloResponse::setIsWritablePrimary(bool isWritablePrimary) { + _isWritablePrimarySet = true; + _isWritablePrimary = isWritablePrimary; +} + +void HelloResponse::setIsSecondary(bool secondary) { + _isSecondarySet = true; + _secondary = secondary; +} + +void HelloResponse::setReplSetName(StringData setName) { + _setNameSet = true; + _setName = setName.toString(); +} + +void HelloResponse::setReplSetVersion(long long version) { + _setVersionSet = true; + _setVersion = version; +} + +void HelloResponse::addHost(const HostAndPort& host) { + _hostsSet = true; + _hosts.push_back(host); +} + +void HelloResponse::addPassive(const HostAndPort& passive) { + _passivesSet = true; + _passives.push_back(passive); +} + +void HelloResponse::addArbiter(const HostAndPort& arbiter) { + _arbitersSet = true; + _arbiters.push_back(arbiter); +} + +void HelloResponse::setPrimary(const HostAndPort& primary) { + _primarySet = true; + _primary = primary; +} + +void HelloResponse::setIsArbiterOnly(bool arbiterOnly) { + _arbiterOnlySet = true; + _arbiterOnly = arbiterOnly; +} + +void HelloResponse::setIsPassive(bool passive) { + _passiveSet = true; + _passive = passive; +} + +void HelloResponse::setIsHidden(bool hidden) { + _hiddenSet = true; + _hidden = hidden; +} + +void HelloResponse::setShouldBuildIndexes(bool buildIndexes) { + _buildIndexesSet = true; + _buildIndexes = buildIndexes; +} + +void HelloResponse::setTopologyVersion(TopologyVersion topologyVersion) { + _topologyVersion = topologyVersion; +} + +void HelloResponse::setSecondaryDelaySecs(Seconds secondaryDelaySecs) { + _secondaryDelaySecsSet = true; + _secondaryDelaySecs = secondaryDelaySecs; +} + +void HelloResponse::addTag(const std::string& tagKey, const std::string& tagValue) { + _tagsSet = true; + _tags[tagKey] = tagValue; +} + +void HelloResponse::setMe(const HostAndPort& me) { + _meSet = true; + _me = me; +} + +void HelloResponse::setElectionId(const OID& electionId) { + _electionId = electionId; +} + +void HelloResponse::setLastWrite(const OpTime& lastWriteOpTime, const time_t lastWriteDate) { + _lastWrite = OpTimeWith<time_t>(lastWriteDate, lastWriteOpTime); +} + +void HelloResponse::setLastMajorityWrite(const OpTime& lastMajorityWriteOpTime, + const time_t lastMajorityWriteDate) { + _lastMajorityWrite = OpTimeWith<time_t>(lastMajorityWriteDate, lastMajorityWriteOpTime); +} + +void HelloResponse::markAsNoConfig() { + _configSet = false; +} + +void HelloResponse::markAsShutdownInProgress() { + _shutdownInProgress = true; +} + +} // namespace repl +} // namespace mongo |