const BSONObj& ReadPreferenceSetting::secondaryPreferredMetadata() { // This is a static method rather than a static member only because it is used by another TU // during dynamic init. static const auto bson = ReadPreferenceSetting(ReadPreference::SecondaryPreferred).toContainingBSON(); return bson; }
StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromContainingBSON( const BSONObj& obj, ReadPreference defaultReadPref) { if (auto elem = obj["$readPreference"]) { return fromInnerBSON(elem); } return ReadPreferenceSetting(defaultReadPref); }
StatusWith<ReadPreferenceSetting> ClusterFind::extractUnwrappedReadPref(const BSONObj& cmdObj, const bool isSlaveOk) { BSONElement queryOptionsElt; auto status = bsonExtractTypedField( cmdObj, QueryRequest::kUnwrappedReadPrefField, BSONType::Object, &queryOptionsElt); if (status.isOK()) { // There must be a nested object containing the read preference if there is a queryOptions // field. BSONObj queryOptionsObj = queryOptionsElt.Obj(); invariant(queryOptionsObj[QueryRequest::kWrappedReadPrefField].type() == BSONType::Object); BSONObj readPrefObj = queryOptionsObj[QueryRequest::kWrappedReadPrefField].Obj(); auto readPref = ReadPreferenceSetting::fromBSON(readPrefObj); if (!readPref.isOK()) { return readPref.getStatus(); } return readPref.getValue(); } else if (status != ErrorCodes::NoSuchKey) { return status; } // If there is no explicit read preference, the value we use depends on the setting of the slave // ok bit. ReadPreference pref = isSlaveOk ? mongo::ReadPreference::SecondaryPreferred : mongo::ReadPreference::PrimaryOnly; return ReadPreferenceSetting(pref, TagSet()); }
StatusWith<CachedDatabaseInfo> createShardDatabase(OperationContext* opCtx, StringData dbName) { auto dbStatus = Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, dbName); if (dbStatus == ErrorCodes::NamespaceNotFound) { ConfigsvrCreateDatabase configCreateDatabaseRequest(dbName.toString()); configCreateDatabaseRequest.setDbName(NamespaceString::kAdminDb); auto configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard(); auto createDbStatus = uassertStatusOK(configShard->runCommandWithFixedRetryAttempts( opCtx, ReadPreferenceSetting(ReadPreference::PrimaryOnly), "admin", CommandHelpers::appendMajorityWriteConcern( configCreateDatabaseRequest.toBSON({})), Shard::RetryPolicy::kIdempotent)) .commandStatus; if (createDbStatus.isOK() || createDbStatus == ErrorCodes::NamespaceExists) { dbStatus = Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, dbName); } else { dbStatus = createDbStatus; } } if (dbStatus.isOK()) { return dbStatus; } return dbStatus.getStatus().withContext(str::stream() << "Database " << dbName << " not found"); }
OpMsgRequest upconvertRequest(StringData db, BSONObj cmdObj, int queryFlags) { cmdObj = cmdObj.getOwned(); // Usually this is a no-op since it is already owned. auto readPrefContainer = BSONObj(); const StringData firstFieldName = cmdObj.firstElementFieldName(); if (firstFieldName == "$query" || firstFieldName == "query") { // Commands sent over OP_QUERY specify read preference by putting it at the top level and // putting the command in a nested field called either query or $query. // Check if legacyCommand has an invalid $maxTimeMS option. uassert(ErrorCodes::InvalidOptions, "cannot use $maxTimeMS query option with commands; use maxTimeMS command option " "instead", !cmdObj.hasField("$maxTimeMS")); if (auto readPref = cmdObj["$readPreference"]) readPrefContainer = readPref.wrap(); cmdObj = cmdObj.firstElement().Obj().shareOwnershipWith(cmdObj); } else if (auto queryOptions = cmdObj["$queryOptions"]) { // Mongos rewrites commands with $readPreference to put it in a field nested inside of // $queryOptions. Its command implementations often forward commands in that format to // shards. This function is responsible for rewriting it to a format that the shards // understand. readPrefContainer = queryOptions.Obj().shareOwnershipWith(cmdObj); cmdObj = cmdObj.removeField("$queryOptions"); } if (!readPrefContainer.isEmpty()) { cmdObj = BSONObjBuilder(std::move(cmdObj)).appendElements(readPrefContainer).obj(); } else if (!cmdObj.hasField("$readPreference") && (queryFlags & QueryOption_SlaveOk)) { BSONObjBuilder bodyBuilder(std::move(cmdObj)); ReadPreferenceSetting(ReadPreference::SecondaryPreferred).toContainingBSON(&bodyBuilder); cmdObj = bodyBuilder.obj(); } // Try to move supported array fields into document sequences. auto docSequenceIt = docSequenceFieldsForCommands.find(cmdObj.firstElementFieldName()); auto docSequenceElem = docSequenceIt == docSequenceFieldsForCommands.end() ? BSONElement() : cmdObj[docSequenceIt->second]; if (!isArrayOfObjects(docSequenceElem)) return OpMsgRequest::fromDBAndBody(db, std::move(cmdObj)); auto docSequenceName = docSequenceElem.fieldNameStringData(); // Note: removing field before adding "$db" to avoid the need to copy the potentially large // array. auto out = OpMsgRequest::fromDBAndBody(db, cmdObj.removeField(docSequenceName)); out.sequences.push_back({docSequenceName.toString()}); for (auto elem : docSequenceElem.Obj()) { out.sequences[0].objs.push_back(elem.Obj().shareOwnershipWith(cmdObj)); } return out; }
StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromBSON(const BSONObj& readPrefObj) { std::string modeStr; auto modeExtractStatus = bsonExtractStringField(readPrefObj, kModeFieldName, &modeStr); if (!modeExtractStatus.isOK()) { return modeExtractStatus; } ReadPreference mode; auto swReadPrefMode = parseReadPreferenceMode(modeStr); if (!swReadPrefMode.isOK()) { return swReadPrefMode.getStatus(); } mode = std::move(swReadPrefMode.getValue()); TagSet tags; BSONElement tagsElem; auto tagExtractStatus = bsonExtractTypedField(readPrefObj, kTagsFieldName, mongo::Array, &tagsElem); if (tagExtractStatus.isOK()) { tags = TagSet{BSONArray(tagsElem.Obj().getOwned())}; // In accordance with the read preference spec, passing the default wildcard tagset // '[{}]' is the same as not passing a TagSet at all. Furthermore, passing an empty // TagSet with a non-primary ReadPreference is equivalent to passing the wildcard // ReadPreference. if (tags == TagSet() || tags == TagSet::primaryOnly()) { tags = defaultTagSetForMode(mode); } // If we are using a user supplied TagSet, check that it is compatible with // the readPreference mode. else if (ReadPreference::PrimaryOnly == mode && (tags != TagSet::primaryOnly())) { return Status(ErrorCodes::BadValue, "Only empty tags are allowed with primary read preference"); } } else if (ErrorCodes::NoSuchKey == tagExtractStatus) { tags = defaultTagSetForMode(mode); } else { return tagExtractStatus; } return ReadPreferenceSetting(mode, tags); }
StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromInnerBSON(const BSONObj& readPrefObj) { std::string modeStr; auto modeExtractStatus = bsonExtractStringField(readPrefObj, kModeFieldName, &modeStr); if (!modeExtractStatus.isOK()) { return modeExtractStatus; } ReadPreference mode; auto swReadPrefMode = parseReadPreferenceMode(modeStr); if (!swReadPrefMode.isOK()) { return swReadPrefMode.getStatus(); } mode = std::move(swReadPrefMode.getValue()); TagSet tags; BSONElement tagsElem; auto tagExtractStatus = bsonExtractTypedField(readPrefObj, kTagsFieldName, mongo::Array, &tagsElem); if (tagExtractStatus.isOK()) { tags = TagSet{BSONArray(tagsElem.Obj().getOwned())}; // In accordance with the read preference spec, passing the default wildcard tagset // '[{}]' is the same as not passing a TagSet at all. Furthermore, passing an empty // TagSet with a non-primary ReadPreference is equivalent to passing the wildcard // ReadPreference. if (tags == TagSet() || tags == TagSet::primaryOnly()) { tags = defaultTagSetForMode(mode); } // If we are using a user supplied TagSet, check that it is compatible with // the readPreference mode. else if (ReadPreference::PrimaryOnly == mode && (tags != TagSet::primaryOnly())) { return Status(ErrorCodes::BadValue, "Only empty tags are allowed with primary read preference"); } } else if (ErrorCodes::NoSuchKey == tagExtractStatus) { tags = defaultTagSetForMode(mode); } else { return tagExtractStatus; } long long maxStalenessSecondsValue; auto maxStalenessSecondsExtractStatus = bsonExtractIntegerFieldWithDefault( readPrefObj, kMaxStalenessSecondsFieldName, 0, &maxStalenessSecondsValue); if (!maxStalenessSecondsExtractStatus.isOK()) { return maxStalenessSecondsExtractStatus; } if (maxStalenessSecondsValue && maxStalenessSecondsValue < 0) { return Status(ErrorCodes::BadValue, str::stream() << kMaxStalenessSecondsFieldName << " must be a non-negative integer"); } if (maxStalenessSecondsValue && maxStalenessSecondsValue >= Seconds::max().count()) { return Status(ErrorCodes::BadValue, str::stream() << kMaxStalenessSecondsFieldName << " value can not exceed " << Seconds::max().count()); } if (maxStalenessSecondsValue && maxStalenessSecondsValue < kMinimalMaxStalenessValue.count()) { return Status(ErrorCodes::MaxStalenessOutOfRange, str::stream() << kMaxStalenessSecondsFieldName << " value can not be less than " << kMinimalMaxStalenessValue.count()); } if ((mode == ReadPreference::PrimaryOnly) && maxStalenessSecondsValue) { return Status(ErrorCodes::BadValue, str::stream() << kMaxStalenessSecondsFieldName << " can not be set for the primary mode"); } return ReadPreferenceSetting(mode, tags, Seconds(maxStalenessSecondsValue)); }