/** * Copyright (C) 2014 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * 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 GNU Affero General 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::kReplication #include "mongo/platform/basic.h" #include "mongo/db/repl/repl_set_heartbeat_response.h" #include #include "mongo/base/status.h" #include "mongo/db/jsobj.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" namespace mongo { namespace repl { namespace { const std::string kOkFieldName = "ok"; const std::string kErrMsgFieldName = "errmsg"; const std::string kErrorCodeFieldName = "code"; const std::string kOpTimeFieldName = "opTime"; const std::string kTimeFieldName = "time"; const std::string kElectionTimeFieldName = "electionTime"; const std::string kConfigFieldName = "config"; const std::string kIsElectableFieldName = "e"; const std::string kMismatchFieldName = "mismatch"; const std::string kIsReplSetFieldName = "rs"; const std::string kHasStateDisagreementFieldName = "stateDisagreement"; const std::string kMemberStateFieldName = "state"; const std::string kConfigVersionFieldName = "v"; const std::string kHbMessageFieldName = "hbmsg"; const std::string kReplSetFieldName = "set"; const std::string kSyncSourceFieldName = "syncingTo"; const std::string kHasDataFieldName = "hasData"; } // namespace ReplSetHeartbeatResponse::ReplSetHeartbeatResponse() : _electionTimeSet(false), _timeSet(false), _time(0), _opTimeSet(false), _electableSet(false), _electable(false), _hasDataSet(false), _hasData(false), _mismatch(false), _isReplSet(false), _stateDisagreement(false), _stateSet(false), _version(-1), _configSet(false) {} void ReplSetHeartbeatResponse::addToBSON(BSONObjBuilder* builder) const { if (_mismatch) { *builder << kOkFieldName << 0.0; *builder << kMismatchFieldName << _mismatch; return; } builder->append(kOkFieldName, 1.0); if (_opTimeSet) { builder->appendDate(kOpTimeFieldName, _opTime.asDate()); } if (_timeSet) { *builder << kTimeFieldName << _time.total_seconds(); } if (_electionTimeSet) { builder->appendDate(kElectionTimeFieldName, _electionTime.asDate()); } if (_configSet) { *builder << kConfigFieldName << _config.toBSON(); } if (_electableSet) { *builder << kIsElectableFieldName << _electable; } if (_isReplSet) { *builder << "rs" << _isReplSet; } if (_stateDisagreement) { *builder << kHasStateDisagreementFieldName << _stateDisagreement; } if (_stateSet) { builder->appendIntOrLL(kMemberStateFieldName, _state.s); } if (_version != -1) { *builder << kConfigVersionFieldName << _version; } *builder << kHbMessageFieldName << _hbmsg; if (!_setName.empty()) { *builder << kReplSetFieldName << _setName; } if (!_syncingTo.empty()) { *builder << kSyncSourceFieldName << _syncingTo; } if (_hasDataSet) { builder->append(kHasDataFieldName, _hasData); } } BSONObj ReplSetHeartbeatResponse::toBSON() const { BSONObjBuilder builder; addToBSON(&builder); return builder.obj(); } Status ReplSetHeartbeatResponse::initialize(const BSONObj& doc) { // Old versions set this even though they returned not "ok" _mismatch = doc[kMismatchFieldName].trueValue(); if (_mismatch) return Status(ErrorCodes::InconsistentReplicaSetNames, "replica set name doesn't match."); // Old versions sometimes set the replica set name ("set") but ok:0 const BSONElement replSetNameElement = doc[kReplSetFieldName]; if (replSetNameElement.eoo()) { _setName.clear(); } else if (replSetNameElement.type() != String) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kReplSetFieldName << "\" field in response to replSetHeartbeat to have " "type String, but found " << typeName(replSetNameElement.type())); } else { _setName = replSetNameElement.String(); } if (_setName.empty() && !doc[kOkFieldName].trueValue()) { std::string errMsg = doc[kErrMsgFieldName].str(); BSONElement errCodeElem = doc[kErrorCodeFieldName]; if (errCodeElem.ok()) { if (!errCodeElem.isNumber()) return Status(ErrorCodes::BadValue, "Error code is not a number!"); int errorCode = errCodeElem.numberInt(); return Status(ErrorCodes::Error(errorCode), errMsg); } return Status(ErrorCodes::UnknownError, errMsg); } const BSONElement hasDataElement = doc[kHasDataFieldName]; _hasDataSet = !hasDataElement.eoo(); _hasData = hasDataElement.trueValue(); const BSONElement electionTimeElement = doc[kElectionTimeFieldName]; if (electionTimeElement.eoo()) { _electionTimeSet = false; } else if (electionTimeElement.type() == Timestamp) { _electionTimeSet = true; _electionTime = electionTimeElement._opTime(); } else if (electionTimeElement.type() == Date) { _electionTimeSet = true; _electionTime = OpTime(electionTimeElement.date()); } else { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kElectionTimeFieldName << "\" field in response to replSetHeartbeat " "command to have type Date or Timestamp, but found type " << typeName(electionTimeElement.type())); } const BSONElement timeElement = doc[kTimeFieldName]; if (timeElement.eoo()) { _timeSet = false; } else if (timeElement.isNumber()) { _timeSet = true; _time = Seconds(timeElement.numberLong()); } else { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kTimeFieldName << "\" field in response to replSetHeartbeat " "command to have a numeric type, but found type " << typeName(timeElement.type())); } const BSONElement opTimeElement = doc[kOpTimeFieldName]; if (opTimeElement.eoo()) { _opTimeSet = false; } else if (opTimeElement.type() == Timestamp) { _opTimeSet = true; _opTime = opTimeElement._opTime(); } else if (opTimeElement.type() == Date) { _opTimeSet = true; _opTime = OpTime(opTimeElement.date()); } else { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kOpTimeFieldName << "\" field in response to replSetHeartbeat " "command to have type Date or Timestamp, but found type " << typeName(opTimeElement.type())); } const BSONElement electableElement = doc[kIsElectableFieldName]; if (electableElement.eoo()) { _electableSet = false; } else { _electableSet = true; _electable = electableElement.trueValue(); } _isReplSet = doc[kIsReplSetFieldName].trueValue(); const BSONElement memberStateElement = doc[kMemberStateFieldName]; if (memberStateElement.eoo()) { _stateSet = false; } else if (memberStateElement.type() != NumberInt && memberStateElement.type() != NumberLong) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kMemberStateFieldName << "\" field in response to replSetHeartbeat " "command to have type NumberInt or NumberLong, but found type " << typeName(memberStateElement.type())); } else { long long stateInt = memberStateElement.numberLong(); if (stateInt < 0 || stateInt > MemberState::RS_MAX) { return Status(ErrorCodes::BadValue, str::stream() << "Value for \"" << kMemberStateFieldName << "\" in response to replSetHeartbeat is " "out of range; legal values are non-negative and no more than " << MemberState::RS_MAX); } _stateSet = true; _state = MemberState(static_cast(stateInt)); } _stateDisagreement = doc[kHasStateDisagreementFieldName].trueValue(); // Not required for the case of uninitialized members -- they have no config const BSONElement versionElement = doc[kConfigVersionFieldName]; // If we have an optime then we must have a version if (_opTimeSet && versionElement.eoo()) { return Status(ErrorCodes::NoSuchKey, str::stream() << "Response to replSetHeartbeat missing required \"" << kConfigVersionFieldName << "\" field even though initialized"); } // If there is a "v" (config version) then it must be an int. if (!versionElement.eoo() && versionElement.type() != NumberInt) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kConfigVersionFieldName << "\" field in response to replSetHeartbeat to have " "type NumberInt, but found " << typeName(versionElement.type())); } _version = versionElement.numberInt(); const BSONElement hbMsgElement = doc[kHbMessageFieldName]; if (hbMsgElement.eoo()) { _hbmsg.clear(); } else if (hbMsgElement.type() != String) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kHbMessageFieldName << "\" field in response to replSetHeartbeat to have " "type String, but found " << typeName(hbMsgElement.type())); } else { _hbmsg = hbMsgElement.String(); } const BSONElement syncingToElement = doc[kSyncSourceFieldName]; if (syncingToElement.eoo()) { _syncingTo.clear(); } else if (syncingToElement.type() != String) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kSyncSourceFieldName << "\" field in response to replSetHeartbeat to " "have type String, but found " << typeName(syncingToElement.type())); } else { _syncingTo = syncingToElement.String(); } const BSONElement rsConfigElement = doc[kConfigFieldName]; if (rsConfigElement.eoo()) { _configSet = false; _config = ReplicaSetConfig(); return Status::OK(); } else if (rsConfigElement.type() != Object) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << kConfigFieldName << "\" in response to replSetHeartbeat to have type " "Object, but found " << typeName(rsConfigElement.type())); } _configSet = true; return _config.initialize(rsConfigElement.Obj()); } MemberState ReplSetHeartbeatResponse::getState() const { invariant(_stateSet); return _state; } OpTime ReplSetHeartbeatResponse::getElectionTime() const { invariant(_electionTimeSet); return _electionTime; } bool ReplSetHeartbeatResponse::isElectable() const { invariant(_electableSet); return _electable; } Seconds ReplSetHeartbeatResponse::getTime() const { invariant(_timeSet); return _time; } OpTime ReplSetHeartbeatResponse::getOpTime() const { invariant(_opTimeSet); return _opTime; } const ReplicaSetConfig& ReplSetHeartbeatResponse::getConfig() const { invariant(_configSet); return _config; } } // namespace repl } // namespace mongo