diff options
Diffstat (limited to 'src/mongo/db/repl/repl_set_heartbeat_response.cpp')
-rw-r--r-- | src/mongo/db/repl/repl_set_heartbeat_response.cpp | 618 |
1 files changed, 304 insertions, 314 deletions
diff --git a/src/mongo/db/repl/repl_set_heartbeat_response.cpp b/src/mongo/db/repl/repl_set_heartbeat_response.cpp index 43b62a43f48..d2a77c58a5f 100644 --- a/src/mongo/db/repl/repl_set_heartbeat_response.cpp +++ b/src/mongo/db/repl/repl_set_heartbeat_response.cpp @@ -45,345 +45,335 @@ namespace mongo { namespace repl { namespace { - const std::string kConfigFieldName = "config"; - const std::string kConfigVersionFieldName = "v"; - const std::string kElectionTimeFieldName = "electionTime"; - const std::string kErrMsgFieldName = "errmsg"; - const std::string kErrorCodeFieldName = "code"; - const std::string kHasDataFieldName = "hasData"; - const std::string kHasStateDisagreementFieldName = "stateDisagreement"; - const std::string kHbMessageFieldName = "hbmsg"; - const std::string kIsElectableFieldName = "e"; - const std::string kIsReplSetFieldName = "rs"; - const std::string kMemberStateFieldName = "state"; - const std::string kMismatchFieldName = "mismatch"; - const std::string kOkFieldName = "ok"; - const std::string kOpTimeFieldName = "opTime"; - const std::string kPrimaryIdFieldName = "primaryId"; - const std::string kReplSetFieldName = "set"; - const std::string kSyncSourceFieldName = "syncingTo"; - const std::string kTermFieldName = "term"; - const std::string kTimeFieldName = "time"; - const std::string kTimestampFieldName = "ts"; +const std::string kConfigFieldName = "config"; +const std::string kConfigVersionFieldName = "v"; +const std::string kElectionTimeFieldName = "electionTime"; +const std::string kErrMsgFieldName = "errmsg"; +const std::string kErrorCodeFieldName = "code"; +const std::string kHasDataFieldName = "hasData"; +const std::string kHasStateDisagreementFieldName = "stateDisagreement"; +const std::string kHbMessageFieldName = "hbmsg"; +const std::string kIsElectableFieldName = "e"; +const std::string kIsReplSetFieldName = "rs"; +const std::string kMemberStateFieldName = "state"; +const std::string kMismatchFieldName = "mismatch"; +const std::string kOkFieldName = "ok"; +const std::string kOpTimeFieldName = "opTime"; +const std::string kPrimaryIdFieldName = "primaryId"; +const std::string kReplSetFieldName = "set"; +const std::string kSyncSourceFieldName = "syncingTo"; +const std::string kTermFieldName = "term"; +const std::string kTimeFieldName = "time"; +const std::string kTimestampFieldName = "ts"; } // namespace - void ReplSetHeartbeatResponse::addToBSON(BSONObjBuilder* builder, - bool isProtocolVersionV1) const { - if (_mismatch) { - *builder << kOkFieldName << 0.0; - *builder << kMismatchFieldName << _mismatch; - return; - } - - builder->append(kOkFieldName, 1.0); - if (_timeSet) { - *builder << kTimeFieldName << durationCount<Seconds>(_time); - } - if (_electionTimeSet) { - builder->appendDate(kElectionTimeFieldName, - Date_t::fromMillisSinceEpoch(_electionTime.asLL())); - } - 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 (_configVersion != -1) { - *builder << kConfigVersionFieldName << _configVersion; - } - *builder << kHbMessageFieldName << _hbmsg; - if (!_setName.empty()) { - *builder << kReplSetFieldName << _setName; - } - if (!_syncingTo.empty()) { - *builder << kSyncSourceFieldName << _syncingTo.toString(); - } - if (_hasDataSet) { - builder->append(kHasDataFieldName, _hasData); - } - if (_term != -1) { - builder->append(kTermFieldName, _term); - } - if (_primaryIdSet) { - builder->append(kPrimaryIdFieldName, _primaryId); - } - if (_opTimeSet) { - if (isProtocolVersionV1) { - BSONObjBuilder opTime(builder->subobjStart(kOpTimeFieldName)); - opTime.append(kTimestampFieldName, _opTime.getTimestamp()); - opTime.append(kTermFieldName, _opTime.getTerm()); - opTime.done(); - } - else { - builder->appendDate(kOpTimeFieldName, - Date_t::fromMillisSinceEpoch(_opTime.getTimestamp().asLL())); - } - } - +void ReplSetHeartbeatResponse::addToBSON(BSONObjBuilder* builder, bool isProtocolVersionV1) const { + if (_mismatch) { + *builder << kOkFieldName << 0.0; + *builder << kMismatchFieldName << _mismatch; + return; } - BSONObj ReplSetHeartbeatResponse::toBSON(bool isProtocolVersionV1) const { - BSONObjBuilder builder; - addToBSON(&builder, isProtocolVersionV1); - return builder.obj(); + builder->append(kOkFieldName, 1.0); + if (_timeSet) { + *builder << kTimeFieldName << durationCount<Seconds>(_time); } - - Status ReplSetHeartbeatResponse::initialize(const BSONObj& doc, long long term) { - - // 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() == bsonTimestamp) { - _electionTimeSet = true; - _electionTime = electionTimeElement.timestamp(); - } - else if (electionTimeElement.type() == Date) { - _electionTimeSet = true; - _electionTime = Timestamp(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())); - } - - _isReplSet = doc[kIsReplSetFieldName].trueValue(); - - // In order to support both the 3.0(V0) and 3.2(V1) heartbeats we must parse the OpTime - // field based on its type. If it is a Date, we parse it as the timestamp and use - // initialize's term argument to complete the OpTime type. If it is an Object, then it's - // V1 and we construct an OpTime out of its nested fields. - const BSONElement opTimeElement = doc[kOpTimeFieldName]; - if (opTimeElement.eoo()) { - _opTimeSet = false; - } - else if (opTimeElement.type() == bsonTimestamp) { - _opTimeSet = true; - _opTime = OpTime(opTimeElement.timestamp(), term); - } - else if (opTimeElement.type() == Date) { - _opTimeSet = true; - _opTime = OpTime(Timestamp(opTimeElement.date()), term); - } - else if (opTimeElement.type() == Object) { - BSONObj opTime = opTimeElement.Obj(); - Timestamp ts; - Status status = bsonExtractTimestampField(opTime, kTimestampFieldName, &ts); - if (!status.isOK()) - return status; - long long extractedTerm; - status = bsonExtractIntegerField(opTime, kTermFieldName, &extractedTerm); - if (!status.isOK()) - return status; - - _opTimeSet = true; - _opTime = OpTime(ts, extractedTerm); - // since a v1 OpTime was in the response, the member must be part of a replset - _isReplSet = true; - } - 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(); - } - - 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<int>(stateInt)); - } - - _stateDisagreement = doc[kHasStateDisagreementFieldName].trueValue(); - - - // Not required for the case of uninitialized members -- they have no config - const BSONElement configVersionElement = doc[kConfigVersionFieldName]; - - // If we have an optime then we must have a configVersion - if (_opTimeSet && configVersionElement.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 (!configVersionElement.eoo() && configVersionElement.type() != NumberInt) { - return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" << - kConfigVersionFieldName << - "\" field in response to replSetHeartbeat to have " - "type NumberInt, but found " << typeName(configVersionElement.type())); + if (_electionTimeSet) { + builder->appendDate(kElectionTimeFieldName, + Date_t::fromMillisSinceEpoch(_electionTime.asLL())); + } + 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 (_configVersion != -1) { + *builder << kConfigVersionFieldName << _configVersion; + } + *builder << kHbMessageFieldName << _hbmsg; + if (!_setName.empty()) { + *builder << kReplSetFieldName << _setName; + } + if (!_syncingTo.empty()) { + *builder << kSyncSourceFieldName << _syncingTo.toString(); + } + if (_hasDataSet) { + builder->append(kHasDataFieldName, _hasData); + } + if (_term != -1) { + builder->append(kTermFieldName, _term); + } + if (_primaryIdSet) { + builder->append(kPrimaryIdFieldName, _primaryId); + } + if (_opTimeSet) { + if (isProtocolVersionV1) { + BSONObjBuilder opTime(builder->subobjStart(kOpTimeFieldName)); + opTime.append(kTimestampFieldName, _opTime.getTimestamp()); + opTime.append(kTermFieldName, _opTime.getTerm()); + opTime.done(); + } else { + builder->appendDate(kOpTimeFieldName, + Date_t::fromMillisSinceEpoch(_opTime.getTimestamp().asLL())); } - _configVersion = configVersionElement.numberInt(); + } +} + +BSONObj ReplSetHeartbeatResponse::toBSON(bool isProtocolVersionV1) const { + BSONObjBuilder builder; + addToBSON(&builder, isProtocolVersionV1); + return builder.obj(); +} + +Status ReplSetHeartbeatResponse::initialize(const BSONObj& doc, long long term) { + // 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(); + } - 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(); - } + if (_setName.empty() && !doc[kOkFieldName].trueValue()) { + std::string errMsg = doc[kErrMsgFieldName].str(); - const BSONElement syncingToElement = doc[kSyncSourceFieldName]; - if (syncingToElement.eoo()) { - _syncingTo = HostAndPort(); - } - 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 = HostAndPort(syncingToElement.String()); - } + BSONElement errCodeElem = doc[kErrorCodeFieldName]; + if (errCodeElem.ok()) { + if (!errCodeElem.isNumber()) + return Status(ErrorCodes::BadValue, "Error code is not a number!"); - const BSONElement rsConfigElement = doc[kConfigFieldName]; - if (rsConfigElement.eoo()) { - _configSet = false; - _config = ReplicaSetConfig(); - return Status::OK(); + int errorCode = errCodeElem.numberInt(); + return Status(ErrorCodes::Error(errorCode), errMsg); } - 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 Status(ErrorCodes::UnknownError, errMsg); + } - return _config.initialize(rsConfigElement.Obj()); + 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() == bsonTimestamp) { + _electionTimeSet = true; + _electionTime = electionTimeElement.timestamp(); + } else if (electionTimeElement.type() == Date) { + _electionTimeSet = true; + _electionTime = Timestamp(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())); } - MemberState ReplSetHeartbeatResponse::getState() const { - invariant(_stateSet); - return _state; + 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())); } - Timestamp ReplSetHeartbeatResponse::getElectionTime() const { - invariant(_electionTimeSet); - return _electionTime; + _isReplSet = doc[kIsReplSetFieldName].trueValue(); + + // In order to support both the 3.0(V0) and 3.2(V1) heartbeats we must parse the OpTime + // field based on its type. If it is a Date, we parse it as the timestamp and use + // initialize's term argument to complete the OpTime type. If it is an Object, then it's + // V1 and we construct an OpTime out of its nested fields. + const BSONElement opTimeElement = doc[kOpTimeFieldName]; + if (opTimeElement.eoo()) { + _opTimeSet = false; + } else if (opTimeElement.type() == bsonTimestamp) { + _opTimeSet = true; + _opTime = OpTime(opTimeElement.timestamp(), term); + } else if (opTimeElement.type() == Date) { + _opTimeSet = true; + _opTime = OpTime(Timestamp(opTimeElement.date()), term); + } else if (opTimeElement.type() == Object) { + BSONObj opTime = opTimeElement.Obj(); + Timestamp ts; + Status status = bsonExtractTimestampField(opTime, kTimestampFieldName, &ts); + if (!status.isOK()) + return status; + long long extractedTerm; + status = bsonExtractIntegerField(opTime, kTermFieldName, &extractedTerm); + if (!status.isOK()) + return status; + + _opTimeSet = true; + _opTime = OpTime(ts, extractedTerm); + // since a v1 OpTime was in the response, the member must be part of a replset + _isReplSet = true; + } 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())); } - bool ReplSetHeartbeatResponse::isElectable() const { - invariant(_electableSet); - return _electable; + const BSONElement electableElement = doc[kIsElectableFieldName]; + if (electableElement.eoo()) { + _electableSet = false; + } else { + _electableSet = true; + _electable = electableElement.trueValue(); } - Seconds ReplSetHeartbeatResponse::getTime() const { - invariant(_timeSet); - return _time; + 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<int>(stateInt)); } - const ReplicaSetConfig& ReplSetHeartbeatResponse::getConfig() const { - invariant(_configSet); - return _config; + _stateDisagreement = doc[kHasStateDisagreementFieldName].trueValue(); + + + // Not required for the case of uninitialized members -- they have no config + const BSONElement configVersionElement = doc[kConfigVersionFieldName]; + + // If we have an optime then we must have a configVersion + if (_opTimeSet && configVersionElement.eoo()) { + return Status(ErrorCodes::NoSuchKey, + str::stream() << "Response to replSetHeartbeat missing required \"" + << kConfigVersionFieldName + << "\" field even though initialized"); } - long long ReplSetHeartbeatResponse::getPrimaryId() const { - invariant(_primaryIdSet); - return _primaryId; + // If there is a "v" (config version) then it must be an int. + if (!configVersionElement.eoo() && configVersionElement.type() != NumberInt) { + return Status(ErrorCodes::TypeMismatch, + str::stream() << "Expected \"" << kConfigVersionFieldName + << "\" field in response to replSetHeartbeat to have " + "type NumberInt, but found " + << typeName(configVersionElement.type())); + } + _configVersion = configVersionElement.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(); } - OpTime ReplSetHeartbeatResponse::getOpTime() const { - invariant(_opTimeSet); - return _opTime; + const BSONElement syncingToElement = doc[kSyncSourceFieldName]; + if (syncingToElement.eoo()) { + _syncingTo = HostAndPort(); + } 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 = HostAndPort(syncingToElement.String()); } -} // namespace repl -} // namespace mongo + 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; +} + +Timestamp ReplSetHeartbeatResponse::getElectionTime() const { + invariant(_electionTimeSet); + return _electionTime; +} + +bool ReplSetHeartbeatResponse::isElectable() const { + invariant(_electableSet); + return _electable; +} + +Seconds ReplSetHeartbeatResponse::getTime() const { + invariant(_timeSet); + return _time; +} + +const ReplicaSetConfig& ReplSetHeartbeatResponse::getConfig() const { + invariant(_configSet); + return _config; +} + +long long ReplSetHeartbeatResponse::getPrimaryId() const { + invariant(_primaryIdSet); + return _primaryId; +} + +OpTime ReplSetHeartbeatResponse::getOpTime() const { + invariant(_opTimeSet); + return _opTime; +} + +} // namespace repl +} // namespace mongo |