Status OldUpdatePositionArgs::initialize(const BSONObj& argsObj) { Status status = bsonCheckOnlyHasFieldsForCommand( "OldUpdatePositionArgs", argsObj, kLegalUpdatePositionFieldNames); if (!status.isOK()) return status; // grab the array of changes BSONElement updateArray; status = bsonExtractTypedField(argsObj, kUpdateArrayFieldName, Array, &updateArray); if (!status.isOK()) return status; // now parse each array entry into an update BSONObjIterator i(updateArray.Obj()); while (i.more()) { BSONObj entry = i.next().Obj(); status = bsonCheckOnlyHasFields("UpdateInfoArgs", entry, kLegalUpdateInfoFieldNames); if (!status.isOK()) return status; OpTime opTime; if (entry[kOpTimeFieldName].isABSONObj()) { // In protocol version 1, { ts: <timestamp>, t: term } Status status = bsonExtractOpTimeField(entry, kOpTimeFieldName, &opTime); if (!status.isOK()) return status; } else { Timestamp ts; status = bsonExtractTimestampField(entry, kOpTimeFieldName, &ts); if (!status.isOK()) return status; opTime = OpTime(ts, OpTime::kUninitializedTerm); } if (!status.isOK()) return status; // TODO(spencer): The following three fields are optional in 3.0, but should be made // required or ignored in 3.0 long long cfgver; status = bsonExtractIntegerFieldWithDefault(entry, kConfigVersionFieldName, -1, &cfgver); if (!status.isOK()) return status; OID rid; status = bsonExtractOIDFieldWithDefault(entry, kMemberRIDFieldName, OID(), &rid); if (!status.isOK()) return status; long long memberID; status = bsonExtractIntegerFieldWithDefault(entry, kMemberIdFieldName, -1, &memberID); if (!status.isOK()) return status; _updates.push_back(UpdateInfo(rid, opTime, cfgver, memberID)); } return Status::OK(); }
StatusWith<OpTime> OpTime::parseFromOplogEntry(const BSONObj& obj) { Timestamp ts; Status status = bsonExtractTimestampField(obj, kTimestampFieldName, &ts); if (!status.isOK()) return status; // Default to -1 if the term is absent. long long term; status = bsonExtractIntegerFieldWithDefault(obj, kTermFieldName, kUninitializedTerm, &term); if (!status.isOK()) return status; return OpTime(ts, term); }
StatusWith<LogicalTimeMetadata> LogicalTimeMetadata::readFromMetadata( const BSONElement& metadataElem) { if (metadataElem.eoo()) { return LogicalTimeMetadata(); } const auto& obj = metadataElem.Obj(); Timestamp ts; Status status = bsonExtractTimestampField(obj, kClusterTimeFieldName, &ts); if (!status.isOK()) { return status; } BSONElement signatureElem; status = bsonExtractTypedField(obj, kSignatureFieldName, Object, &signatureElem); if (!status.isOK()) { return status; } const auto& signatureObj = signatureElem.Obj(); // Extract BinData type signature hash and construct a SHA1Block instance from it. BSONElement hashElem; status = bsonExtractTypedField(signatureObj, kSignatureHashFieldName, BinData, &hashElem); if (!status.isOK()) { return status; } int hashLength = 0; auto rawBinSignature = hashElem.binData(hashLength); BSONBinData proofBinData(rawBinSignature, hashLength, hashElem.binDataType()); auto proofStatus = SHA1Block::fromBinData(proofBinData); if (!proofStatus.isOK()) { return proofStatus.getStatus(); } long long keyId; status = bsonExtractIntegerField(signatureObj, kSignatureKeyIdFieldName, &keyId); if (!status.isOK()) { return status; } return LogicalTimeMetadata( SignedLogicalTime(LogicalTime(ts), std::move(proofStatus.getValue()), keyId)); }
Status bsonExtractOpTimeField(const BSONObj& object, StringData fieldName, repl::OpTime* out) { BSONElement element; Status status = bsonExtractTypedField(object, fieldName, Object, &element); if (!status.isOK()) return status; BSONObj opTimeObj = element.Obj(); Timestamp ts; status = bsonExtractTimestampField(opTimeObj, kTimestampFieldName, &ts); if (!status.isOK()) return status; long long term; status = bsonExtractIntegerField(opTimeObj, kTermFieldName, &term); if (!status.isOK()) return status; *out = repl::OpTime(ts, term); return Status::OK(); }
Status ReadAfterOpTimeArgs::initialize(const BSONObj& cmdObj) { auto afterElem = cmdObj[ReadAfterOpTimeArgs::kRootFieldName]; if (afterElem.eoo()) { return Status::OK(); } if (!afterElem.isABSONObj()) { return Status(ErrorCodes::FailedToParse, "'after' field should be an object"); } BSONObj readAfterObj = afterElem.Obj(); BSONElement opTimeElem; auto opTimeStatus = bsonExtractTypedField( readAfterObj, ReadAfterOpTimeArgs::kOpTimeFieldName, Object, &opTimeElem); if (!opTimeStatus.isOK()) { return opTimeStatus; } BSONObj opTimeObj = opTimeElem.Obj(); BSONElement timestampElem; Timestamp timestamp; auto timestampStatus = bsonExtractTimestampField( opTimeObj, ReadAfterOpTimeArgs::kOpTimestampFieldName, ×tamp); if (!timestampStatus.isOK()) { return timestampStatus; } long long termNumber; auto termStatus = bsonExtractIntegerField(opTimeObj, ReadAfterOpTimeArgs::kOpTermFieldName, &termNumber); if (!termStatus.isOK()) { return termStatus; } _opTime = OpTime(timestamp, termNumber); return Status::OK(); }
Status ReplSetRequestVotesArgs::initialize(const BSONObj& argsObj) { Status status = bsonCheckOnlyHasFields("ReplSetRequestVotes", argsObj, kLegalArgsFieldNames); if (!status.isOK()) return status; status = bsonExtractIntegerField(argsObj, kTermFieldName, &_term); if (!status.isOK()) return status; status = bsonExtractIntegerField(argsObj, kCandidateIdFieldName, &_candidateId); if (!status.isOK()) return status; status = bsonExtractIntegerField(argsObj, kConfigVersionFieldName, &_cfgver); if (!status.isOK()) return status; status = bsonExtractStringField(argsObj, kSetNameFieldName, &_setName); if (!status.isOK()) return status; // extracting the lastCommittedOp is a bit of a process BSONObj lastCommittedOp = argsObj[kLastCommittedOpFieldName].Obj(); Timestamp ts; status = bsonExtractTimestampField(lastCommittedOp, kOpTimeFieldName, &ts); if (!status.isOK()) return status; long long term; status = bsonExtractIntegerField(lastCommittedOp, kTermFieldName, &term); if (!status.isOK()) return status; _lastCommittedOp = OpTime(lastCommittedOp[kOpTimeFieldName].timestamp(), lastCommittedOp[kTermFieldName].Long()); return Status::OK(); }
Status ReadConcernArgs::initialize(const BSONElement& readConcernElem) { invariant(isEmpty()); // only legal to call on uninitialized object. if (readConcernElem.eoo()) { return Status::OK(); } dassert(readConcernElem.fieldNameStringData() == kReadConcernFieldName); if (readConcernElem.type() != Object) { return Status(ErrorCodes::FailedToParse, str::stream() << kReadConcernFieldName << " field should be an object"); } BSONObj readConcernObj = readConcernElem.Obj(); for (auto&& field : readConcernObj) { auto fieldName = field.fieldNameStringData(); if (fieldName == kAfterOpTimeFieldName) { OpTime opTime; // TODO pass field in rather than scanning again. auto opTimeStatus = bsonExtractOpTimeField(readConcernObj, kAfterOpTimeFieldName, &opTime); if (!opTimeStatus.isOK()) { return opTimeStatus; } _opTime = opTime; } else if (fieldName == kAfterClusterTimeFieldName) { Timestamp afterClusterTime; auto afterClusterTimeStatus = bsonExtractTimestampField( readConcernObj, kAfterClusterTimeFieldName, &afterClusterTime); if (!afterClusterTimeStatus.isOK()) { return afterClusterTimeStatus; } _afterClusterTime = LogicalTime(afterClusterTime); } else if (fieldName == kAtClusterTimeFieldName) { Timestamp atClusterTime; auto atClusterTimeStatus = bsonExtractTimestampField(readConcernObj, kAtClusterTimeFieldName, &atClusterTime); if (!atClusterTimeStatus.isOK()) { return atClusterTimeStatus; } _atClusterTime = LogicalTime(atClusterTime); } else if (fieldName == kLevelFieldName) { std::string levelString; // TODO pass field in rather than scanning again. auto readCommittedStatus = bsonExtractStringField(readConcernObj, kLevelFieldName, &levelString); if (!readCommittedStatus.isOK()) { return readCommittedStatus; } if (levelString == kLocalReadConcernStr) { _level = ReadConcernLevel::kLocalReadConcern; } else if (levelString == kMajorityReadConcernStr) { _level = ReadConcernLevel::kMajorityReadConcern; } else if (levelString == kLinearizableReadConcernStr) { _level = ReadConcernLevel::kLinearizableReadConcern; } else if (levelString == kAvailableReadConcernStr) { _level = ReadConcernLevel::kAvailableReadConcern; } else if (levelString == kSnapshotReadConcernStr) { _level = ReadConcernLevel::kSnapshotReadConcern; } else { return Status(ErrorCodes::FailedToParse, str::stream() << kReadConcernFieldName << '.' << kLevelFieldName << " must be either 'local', 'majority', " "'linearizable', 'available', or 'snapshot'"); } _originalLevel = _level; } else { return Status(ErrorCodes::InvalidOptions, str::stream() << "Unrecognized option in " << kReadConcernFieldName << ": " << fieldName); } } if (_afterClusterTime && _opTime) { return Status(ErrorCodes::InvalidOptions, str::stream() << "Can not specify both " << kAfterClusterTimeFieldName << " and " << kAfterOpTimeFieldName); } if (_afterClusterTime && _atClusterTime) { return Status(ErrorCodes::InvalidOptions, str::stream() << "Can not specify both " << kAfterClusterTimeFieldName << " and " << kAtClusterTimeFieldName); } // Note: 'available' should not be used with after cluster time, as cluster time can wait for // replication whereas the premise of 'available' is to avoid waiting. 'linearizable' should not // be used with after cluster time, since linearizable reads are inherently causally consistent. if (_afterClusterTime && getLevel() != ReadConcernLevel::kMajorityReadConcern && getLevel() != ReadConcernLevel::kLocalReadConcern && getLevel() != ReadConcernLevel::kSnapshotReadConcern) { return Status(ErrorCodes::InvalidOptions, str::stream() << kAfterClusterTimeFieldName << " field can be set only if " << kLevelFieldName << " is equal to " << kMajorityReadConcernStr << ", " << kLocalReadConcernStr << ", or " << kSnapshotReadConcernStr); } if (_opTime && getLevel() == ReadConcernLevel::kSnapshotReadConcern) { return Status(ErrorCodes::InvalidOptions, str::stream() << kAfterOpTimeFieldName << " field cannot be set if " << kLevelFieldName << " is equal to " << kSnapshotReadConcernStr); } if (_atClusterTime && getLevel() != ReadConcernLevel::kSnapshotReadConcern) { return Status(ErrorCodes::InvalidOptions, str::stream() << kAtClusterTimeFieldName << " field can be set only if " << kLevelFieldName << " is equal to " << kSnapshotReadConcernStr); } if (_afterClusterTime && _afterClusterTime == LogicalTime::kUninitialized) { return Status(ErrorCodes::InvalidOptions, str::stream() << kAfterClusterTimeFieldName << " cannot be a null timestamp"); } if (_atClusterTime && _atClusterTime == LogicalTime::kUninitialized) { return Status(ErrorCodes::InvalidOptions, str::stream() << kAtClusterTimeFieldName << " cannot be a null timestamp"); } return Status::OK(); }
Status ReplSetHeartbeatResponseV1::initialize(const BSONObj& doc) { Status status = bsonCheckOnlyHasFields("ReplSetHeartbeatResponse", doc, kLegalHeartbeatFieldNames); if (!status.isOK()) return status; status = bsonExtractBooleanField(doc, kIsReplSetFieldName, &_isReplSet); if (!status.isOK()) return status; status = bsonExtractStringField(doc, kReplSetFieldName, &_setName); if (!status.isOK()) return status; long long stateInt; status = bsonExtractIntegerField(doc, kMemberStateFieldName, &stateInt); if (!status.isOK()) return status; 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); } _state = MemberState(static_cast<int>(stateInt)); // extracting the lastCommittedOp is a bit of a process BSONObj lastOpTime = doc[kLastOpTimeFieldName].Obj(); Timestamp ts; status = bsonExtractTimestampField(lastOpTime, kOpTimeFieldName, &ts); if (!status.isOK()) return status; long long term; status = bsonExtractIntegerField(lastOpTime, kTermFieldName, &term); if (!status.isOK()) return status; _lastOpTime = OpTime(lastOpTime[kOpTimeFieldName].timestamp(), lastOpTime[kTermFieldName].Long()); status = bsonExtractStringField(doc, kSyncSourceFieldName, &_syncingTo); if (!status.isOK()) return status; status = bsonExtractIntegerField(doc, kConfigVersionFieldName, &_configVersion); if (!status.isOK()) return status; status = bsonExtractIntegerField(doc, kPrimaryIdFieldName, &_primaryId); if (!status.isOK()) return status; status = bsonExtractIntegerField(doc, kTermFieldName, &_term); if (!status.isOK()) return status; const BSONElement hasDataElement = doc[kHasDataFieldName]; _hasDataSet = !hasDataElement.eoo(); _hasData = hasDataElement.trueValue(); 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()); }
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 term; status = bsonExtractIntegerField(opTime, kTermFieldName, &term); if (!status.isOK()) return status; _opTimeSet = true; _opTime = OpTime(ts, term); // 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())); } _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(); } 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()); } 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()); }