Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 73 additions & 37 deletions src/mongo/s/query/cluster_find.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,56 +88,83 @@ static const int kPerDocumentOverheadBytesUpperBound = 10;
* suitable for forwarding to the targeted hosts.
*/
StatusWith<std::unique_ptr<QueryRequest>> transformQueryForShards(
const QueryRequest& qr, bool appendGeoNearDistanceProjection) {
const QueryRequest& qr, bool appendGeoNearDistanceProjection, bool forwardToSingleShard) {
// For multi-shards queries:
// If there is a limit, we forward the sum of the limit and the skip.
// For single-shard queries:
// If there is a limit, we forward the original limit.
boost::optional<long long> newLimit;
if (qr.getLimit()) {
long long newLimitValue;
if (mongoSignedAddOverflow64(*qr.getLimit(), qr.getSkip().value_or(0), &newLimitValue)) {
return Status(
ErrorCodes::Overflow,
str::stream()
<< "sum of limit and skip cannot be represented as a 64-bit integer, limit: "
<< *qr.getLimit()
<< ", skip: "
<< qr.getSkip().value_or(0));
if (forwardToSingleShard) {
newLimit = *qr.getLimit();
} else {
long long newLimitValue;
if (mongoSignedAddOverflow64(*qr.getLimit(), qr.getSkip().value_or(0), &newLimitValue)) {
return Status(
ErrorCodes::Overflow,
str::stream()
<< "sum of limit and skip cannot be represented as a 64-bit integer, limit: "
<< *qr.getLimit()
<< ", skip: "
<< qr.getSkip().value_or(0));
}
newLimit = newLimitValue;
}
newLimit = newLimitValue;
}

// Similarly, if nToReturn is set, we forward the sum of nToReturn and the skip.
// For multi-shards queries:
// If nToReturn is set, we forward the sum of nToReturn and the skip.
// For single-shard queries:
// If nToReturn is set, we forward the original nToReturn.
boost::optional<long long> newNToReturn;
if (qr.getNToReturn()) {
// !wantMore and ntoreturn mean the same as !wantMore and limit, so perform the conversion.
if (!qr.wantMore()) {
long long newLimitValue;
if (mongoSignedAddOverflow64(
*qr.getNToReturn(), qr.getSkip().value_or(0), &newLimitValue)) {
return Status(ErrorCodes::Overflow,
str::stream()
<< "sum of ntoreturn and skip cannot be represented as a 64-bit "
if (forwardToSingleShard) {
newLimit = *qr.getNToReturn();
} else {
long long newLimitValue;
if (mongoSignedAddOverflow64(
*qr.getNToReturn(), qr.getSkip().value_or(0), &newLimitValue)) {
return Status(ErrorCodes::Overflow,
str::stream()
<< "sum of ntoreturn and skip cannot be represented as a 64-bit "
"integer, ntoreturn: "
<< *qr.getNToReturn()
<< ", skip: "
<< qr.getSkip().value_or(0));
<< *qr.getNToReturn()
<< ", skip: "
<< qr.getSkip().value_or(0));
}
newLimit = newLimitValue;
}
newLimit = newLimitValue;
} else {
long long newNToReturnValue;
if (mongoSignedAddOverflow64(
*qr.getNToReturn(), qr.getSkip().value_or(0), &newNToReturnValue)) {
return Status(ErrorCodes::Overflow,
str::stream()
<< "sum of ntoreturn and skip cannot be represented as a 64-bit "
"integer, ntoreturn: "
<< *qr.getNToReturn()
<< ", skip: "
<< qr.getSkip().value_or(0));
if (forwardToSingleShard) {
newNToReturn = *qr.getNToReturn();
} else {
long long newNToReturnValue;
if (mongoSignedAddOverflow64(
*qr.getNToReturn(), qr.getSkip().value_or(0), &newNToReturnValue)) {
return Status(ErrorCodes::Overflow,
str::stream()
<< "sum of ntoreturn and skip cannot be represented as a 64-bit "
"integer, ntoreturn: "
<< *qr.getNToReturn()
<< ", skip: "
<< qr.getSkip().value_or(0));
}
newNToReturn = newNToReturnValue;
}
newNToReturn = newNToReturnValue;
}
}

// For single-shard queries, we forward the original skip to mongod.
// For multi-shards queries or no skip specified, we forward none skip
boost::optional<long long> newSkip;
if (qr.getSkip() && forwardToSingleShard) {
newSkip = *qr.getSkip();
} else {
newSkip = boost::none;
}

// If there is a sort other than $natural, we send a sortKey meta-projection to the remote node.
BSONObj newProjection = qr.getProj();
if (!qr.getSort().isEmpty() && !qr.getSort()["$natural"]) {
Expand All @@ -157,7 +184,7 @@ StatusWith<std::unique_ptr<QueryRequest>> transformQueryForShards(

auto newQR = stdx::make_unique<QueryRequest>(qr);
newQR->setProj(newProjection);
newQR->setSkip(boost::none);
newQR->setSkip(newSkip);
newQR->setLimit(newLimit);
newQR->setNToReturn(newNToReturn);

Expand All @@ -180,9 +207,10 @@ std::vector<std::pair<ShardId, BSONObj>> constructRequestsForShards(
const std::set<ShardId>& shardIds,
const CanonicalQuery& query,
bool appendGeoNearDistanceProjection,
boost::optional<LogicalTime> atClusterTime) {
boost::optional<LogicalTime> atClusterTime,
bool forwardToSingleShard) {
const auto qrToForward = uassertStatusOK(
transformQueryForShards(query.getQueryRequest(), appendGeoNearDistanceProjection));
transformQueryForShards(query.getQueryRequest(), appendGeoNearDistanceProjection, forwardToSingleShard));

if (atClusterTime) {
auto readConcernAtClusterTime =
Expand Down Expand Up @@ -278,11 +306,19 @@ CursorId runQueryWithoutRetrying(OperationContext* opCtx,
// Tailable cursors can't have a sort, which should have already been validated.
invariant(params.sort.isEmpty() || !query.getQueryRequest().isTailable());

bool forwardToSingleShard = false;
if (shardIds.size() == 1) {
LOG(3) << "cluster find command: " << query.toString() <<", forward to a single shard";
forwardToSingleShard = true;
// Skip nothing in mongos, as skip operation is done in mongod if needed.
params.skip = boost::none;
}

// Construct the requests that we will use to establish cursors on the targeted shards,
// attaching the shardVersion and txnNumber, if necessary.

auto requests = constructRequestsForShards(
opCtx, routingInfo, shardIds, query, appendGeoNearDistanceProjection, atClusterTime);
opCtx, routingInfo, shardIds, query, appendGeoNearDistanceProjection, atClusterTime, forwardToSingleShard);

// Establish the cursors with a consistent shardVersion across shards.

Expand Down