summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Swanson <charlie.swanson@mongodb.com>2016-11-02 18:08:23 -0400
committerCharlie Swanson <charlie.swanson@mongodb.com>2016-11-08 10:16:56 -0500
commit524cc9989bcf743b34bcc9e9d05d9f72cae48620 (patch)
treea17f8144564b141ba140dd6494d1260edca21d44
parent7c626977cf052314d09648d1061e07c44e964264 (diff)
downloadmongo-524cc9989bcf743b34bcc9e9d05d9f72cae48620.tar.gz
SERVER-26182 Avoid putting OP_REPLY batches >16MB into a single BSONObj
-rw-r--r--jstests/multiVersion/large_document_sync.js62
-rw-r--r--src/mongo/base/error_codes.err1
-rw-r--r--src/mongo/client/fetcher.cpp173
-rw-r--r--src/mongo/client/remote_command_runner_impl.cpp8
-rw-r--r--src/mongo/executor/downconvert_find_and_getmore_commands.cpp135
-rw-r--r--src/mongo/executor/downconvert_find_and_getmore_commands.h25
-rw-r--r--src/mongo/executor/network_interface_asio_command.cpp7
7 files changed, 243 insertions, 168 deletions
diff --git a/jstests/multiVersion/large_document_sync.js b/jstests/multiVersion/large_document_sync.js
new file mode 100644
index 00000000000..3728ddfa75d
--- /dev/null
+++ b/jstests/multiVersion/large_document_sync.js
@@ -0,0 +1,62 @@
+/**
+ * Tests syncing large objects from a 3.0 node to a 3.2 node. In particular, this test was designed
+ * to stress the logic for upconverting an OP_REPLY that contains more than 16MB worth of documents.
+ *
+ * This test was designed to reproduce SERVER-26182.
+ */
+(function() {
+ // Create a replica set with one "3.0" node and one "3.2" node.
+ var replSetName = "testset";
+ var nodes = [{binVersion: "3.0"}, {binVersion: "latest"}];
+
+ var rst = ReplSetTest({name: replSetName, nodes: nodes, nodeOptions: {vv: ''}});
+ rst.startSet();
+
+ // Rig the election so that the 3.0 node becomes primary.
+ var replSetConfig = rst.getReplSetConfig();
+ replSetConfig.members[1].priority = 0;
+ rst.initiate(replSetConfig);
+
+ var primaryDB = rst.getPrimary().getDB("test");
+
+ primaryDB.c.drop();
+
+ var docCloseTo1MB = {
+ x: new Array(900 * 1024).join("x")
+ };
+ assert.gte(Object.bsonsize(docCloseTo1MB), 0.5 * 1024 * 1024);
+ assert.lt(Object.bsonsize(docCloseTo1MB), 1 * 1024 * 1024);
+
+ var docCloseTo4MB = {
+ x: new Array(3.5 * 1024 * 1024).join("x")
+ };
+ assert.gte(Object.bsonsize(docCloseTo4MB), 3 * 1024 * 1024);
+ assert.lt(Object.bsonsize(docCloseTo4MB), 4 * 1024 * 1024);
+
+ var docCloseTo16MB = {
+ x: new Array(15.5 * 1024 * 1024).join("x")
+ };
+ assert.gte(Object.bsonsize(docCloseTo16MB), 15 * 1024 * 1024);
+ assert.lt(Object.bsonsize(docCloseTo16MB), 16 * 1024 * 1024);
+
+ assert.gt(Object.bsonsize(docCloseTo4MB) + Object.bsonsize(docCloseTo16MB), 16 * 1024 * 1024);
+
+ assert.gt(Object.bsonsize(docCloseTo1MB) + Object.bsonsize(docCloseTo16MB), 16 * 1024 * 1024);
+
+ rst.getPrimary().forceWriteMode("commands");
+ // The first find has a threshold of 1MB, so put almost 1MB in, then a huge
+ // document to make the total over 16MB.
+ assert.writeOK(primaryDB.c.insert(docCloseTo1MB));
+ assert.writeOK(primaryDB.c.insert(docCloseTo16MB));
+
+ // The first getMore batch should contain both the ~4 MB and the ~16 MB
+ // document, leading to more than 16 MB of user data in the batch.
+ assert.writeOK(primaryDB.c.insert(docCloseTo4MB));
+ assert.writeOK(primaryDB.c.insert(docCloseTo16MB));
+
+ rst.awaitReplication();
+ assert.commandWorked(rst.getSecondary().adminCommand({isMaster: 1}),
+ "expected secondary to survive syncing large documents");
+
+ rst.stopSet();
+})();
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 30bf2a8cc1b..7657112cac6 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -155,6 +155,7 @@ error_code("OplogOutOfOrder", 152)
error_code("CanRepairToDowngrade", 157)
error_code("MustUpgrade", 158)
error_code("QueryPlanKilled", 173)
+error_code("ReceivedOpReplyMessage", 198);
# Non-sequential error codes (for compatibility only)
error_code("RecvStaleConfig", 9996)
diff --git a/src/mongo/client/fetcher.cpp b/src/mongo/client/fetcher.cpp
index 97e17d0fbda..aad747945df 100644
--- a/src/mongo/client/fetcher.cpp
+++ b/src/mongo/client/fetcher.cpp
@@ -31,9 +31,12 @@
#include "mongo/client/fetcher.h"
+#include "mongo/base/data_range_cursor.h"
+#include "mongo/base/data_type_validated.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/rpc/get_status_from_command_result.h"
+#include "mongo/rpc/object_check.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
@@ -53,27 +56,19 @@ const char* kNamespaceFieldName = "ns";
const char* kFirstBatchFieldName = "firstBatch";
const char* kNextBatchFieldName = "nextBatch";
-/**
- * Parses cursor response in command result for cursor ID, namespace and documents.
- * 'batchFieldName' will be 'firstBatch' for the initial remote command invocation and
- * 'nextBatch' for getMore.
- */
-Status parseCursorResponse(const BSONObj& obj,
- const std::string& batchFieldName,
- Fetcher::QueryResponse* batchData) {
- invariant(batchFieldName == kFirstBatchFieldName || batchFieldName == kNextBatchFieldName);
- invariant(batchData);
-
- BSONElement cursorElement = obj.getField(kCursorFieldName);
+Status parseCursorResponseFromResponseObj(const BSONObj& responseObj,
+ const std::string& batchFieldName,
+ Fetcher::QueryResponse* batchData) {
+ BSONElement cursorElement = responseObj.getField(kCursorFieldName);
if (cursorElement.eoo()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain '" << kCursorFieldName
- << "' field: " << obj);
+ << "' field: " << responseObj);
}
if (!cursorElement.isABSONObj()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName
- << "' field must be an object: " << obj);
+ << "' field must be an object: " << responseObj);
}
BSONObj cursorObj = cursorElement.Obj();
@@ -81,13 +76,13 @@ Status parseCursorResponse(const BSONObj& obj,
if (cursorIdElement.eoo()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain '" << kCursorFieldName << "."
- << kCursorIdFieldName << "' field: " << obj);
+ << kCursorIdFieldName << "' field: " << responseObj);
}
if (cursorIdElement.type() != mongo::NumberLong) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName << "." << kCursorIdFieldName
<< "' field must be a 'long' but was a '"
- << typeName(cursorIdElement.type()) << "': " << obj);
+ << typeName(cursorIdElement.type()) << "': " << responseObj);
}
batchData->cursorId = cursorIdElement.numberLong();
@@ -96,18 +91,18 @@ Status parseCursorResponse(const BSONObj& obj,
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain "
<< "'" << kCursorFieldName << "." << kNamespaceFieldName
- << "' field: " << obj);
+ << "' field: " << responseObj);
}
if (namespaceElement.type() != mongo::String) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName << "." << kNamespaceFieldName
- << "' field must be a string: " << obj);
+ << "' field must be a string: " << responseObj);
}
NamespaceString tempNss(namespaceElement.valuestrsafe());
if (!tempNss.isValid()) {
return Status(ErrorCodes::BadValue,
str::stream() << "'" << kCursorFieldName << "." << kNamespaceFieldName
- << "' contains an invalid namespace: " << obj);
+ << "' contains an invalid namespace: " << responseObj);
}
batchData->nss = tempNss;
@@ -115,12 +110,12 @@ Status parseCursorResponse(const BSONObj& obj,
if (batchElement.eoo()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain '" << kCursorFieldName << "."
- << batchFieldName << "' field: " << obj);
+ << batchFieldName << "' field: " << responseObj);
}
if (!batchElement.isABSONObj()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName << "." << batchFieldName
- << "' field must be an array: " << obj);
+ << "' field must be an array: " << responseObj);
}
BSONObj batchObj = batchElement.Obj();
for (auto itemElement : batchObj) {
@@ -128,7 +123,7 @@ Status parseCursorResponse(const BSONObj& obj,
return Status(ErrorCodes::FailedToParse,
str::stream() << "found non-object " << itemElement << " in "
<< "'" << kCursorFieldName << "." << batchFieldName
- << "' field: " << obj);
+ << "' field: " << responseObj);
}
batchData->documents.push_back(itemElement.Obj().getOwned());
}
@@ -136,6 +131,130 @@ Status parseCursorResponse(const BSONObj& obj,
return Status::OK();
}
+/**
+ * Extracts the CursorId and array of results from a Message representing an OP_REPLY. Returns a
+ * non-OK status if Message does not represent a well-formed OP_REPLY.
+ */
+StatusWith<std::tuple<CursorId, std::vector<BSONObj>>> getBatchFromReply(const Message* response) {
+ auto header = response->header();
+ if (header.getNetworkOp() != mongo::opReply) {
+ return {ErrorCodes::ProtocolError,
+ str::stream() << "Expected to be decoding an OP_REPLY but got "
+ << mongo::networkOpToString(header.getNetworkOp())};
+ }
+
+ if ((header.dataLen() < 0) ||
+ (static_cast<std::size_t>(header.dataLen()) > mongo::MaxMessageSizeBytes)) {
+ return {ErrorCodes::InvalidLength,
+ str::stream() << "Received message has invalid length field with value "
+ << header.dataLen()};
+ }
+
+ QueryResult::View qr = response->header().view2ptr();
+
+ auto resultFlags = qr.getResultFlags();
+
+ if (resultFlags & ResultFlag_CursorNotFound) {
+ return {ErrorCodes::CursorNotFound,
+ str::stream() << "Cursor with id '" << qr.getCursorId() << "' not found"};
+ }
+
+ // Use CDRC directly instead of DocumentRange as DocumentRange has a throwing API.
+ ConstDataRangeCursor cdrc{qr.data(), qr.data() + header.dataLen()};
+
+ if (resultFlags & ResultFlag_ErrSet) {
+ if (qr.getNReturned() != 1) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "ResultFlag_ErrSet flag set on reply, but nReturned was '"
+ << qr.getNReturned() << "' - expected 1"};
+ }
+ // Convert error document to a Status.
+ // Will throw if first document is invalid BSON.
+ auto first = cdrc.readAndAdvance<Validated<BSONObj>>();
+ if (!first.isOK()) {
+ return first.getStatus();
+ }
+
+ // Convert error document to a status.
+ return getStatusFromCommandResult(first.getValue());
+ }
+
+ const int32_t nReturned = qr.getNReturned();
+ std::vector<BSONObj> batch;
+ batch.reserve(qr.getNReturned());
+
+ int32_t nParsed = 0;
+ Validated<BSONObj> nextObj;
+ while (!cdrc.empty() && nParsed < nReturned) {
+ auto readStatus = cdrc.readAndAdvance(&nextObj);
+ if (!readStatus.isOK()) {
+ return readStatus;
+ }
+ ++nParsed;
+ batch.emplace_back(nextObj.val.getOwned());
+ }
+ if (nParsed != nReturned) {
+ return {ErrorCodes::InvalidLength,
+ str::stream() << "Count of documents in OP_REPLY message (" << nParsed
+ << ") did not match the value specified in the nReturned field ("
+ << nReturned << ")"};
+ }
+
+ return {std::make_tuple(qr.getCursorId(), std::move(batch))};
+}
+
+Status parseCursorResponseFromRawMessage(const Message* message,
+ Fetcher::QueryResponse* batchData) {
+ auto batchStatus = getBatchFromReply(message);
+ if (!batchStatus.isOK()) {
+ return batchStatus.getStatus();
+ }
+
+ std::tie(batchData->cursorId, batchData->documents) = batchStatus.getValue();
+ return Status::OK();
+}
+
+/**
+ * Parses cursor response in command result for cursor ID, namespace and documents.
+ * 'batchFieldName' will be 'firstBatch' for the initial remote command invocation and 'nextBatch'
+ * for getMore.
+ */
+Status parseCursorResponse(const RemoteCommandResponse& response,
+ const std::string& batchFieldName,
+ Fetcher::QueryResponse* batchData) {
+ invariant(batchFieldName == kFirstBatchFieldName || batchFieldName == kNextBatchFieldName);
+ invariant(batchData);
+
+ // If we are talking to a 3.0 mongod, then the response will have come back as an OP_QUERY, and
+ // we'll need to parse the raw message to populate 'batchData'. Otherwise, we ran a find or
+ // getMore command, and need to parse the BSON that is returned from those commands.
+ Status status = getStatusFromCommandResult(response.data);
+ if (status.isOK()) {
+ return parseCursorResponseFromResponseObj(response.data, batchFieldName, batchData);
+ } else if (status.code() == ErrorCodes::ReceivedOpReplyMessage) {
+ auto ns = response.data["ns"];
+ if (!ns) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << "expected 'ns' field to be present in response: "
+ << response.data};
+ }
+ if (ns.type() != String) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << "expected 'ns' field to be a string, was "
+ << typeName(ns.type()) << ": " << response.data};
+ }
+ auto nss = NamespaceString(ns.String());
+ if (!nss.isValid()) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << "invalid 'ns' field in response: " << response.data};
+ }
+ batchData->nss = nss;
+ return parseCursorResponseFromRawMessage(response.message.get(), batchData);
+ } else {
+ return status;
+ }
+}
+
} // namespace
Fetcher::Fetcher(executor::TaskExecutor* executor,
@@ -244,16 +363,8 @@ void Fetcher::_callback(const RemoteCommandCallbackArgs& rcbd, const char* batch
return;
}
- const BSONObj& queryResponseObj = rcbd.response.getValue().data;
- Status status = getStatusFromCommandResult(queryResponseObj);
- if (!status.isOK()) {
- _work(StatusWith<Fetcher::QueryResponse>(status), nullptr, nullptr);
- _finishCallback();
- return;
- }
-
QueryResponse batchData;
- status = parseCursorResponse(queryResponseObj, batchFieldName, &batchData);
+ auto status = parseCursorResponse(rcbd.response.getValue(), batchFieldName, &batchData);
if (!status.isOK()) {
_work(StatusWith<Fetcher::QueryResponse>(status), nullptr, nullptr);
_finishCallback();
diff --git a/src/mongo/client/remote_command_runner_impl.cpp b/src/mongo/client/remote_command_runner_impl.cpp
index e856173a314..7b6caab1805 100644
--- a/src/mongo/client/remote_command_runner_impl.cpp
+++ b/src/mongo/client/remote_command_runner_impl.cpp
@@ -83,7 +83,7 @@ Status getStatusFromCursorResult(DBClientCursor& cursor) {
using RequestDownconverter = StatusWith<Message>(*)(const RemoteCommandRequest&);
using ReplyUpconverter = StatusWith<RemoteCommandResponse>(*)(std::uint32_t requestId,
StringData cursorNamespace,
- const Message& response);
+ Message* response);
template <RequestDownconverter downconvertRequest, ReplyUpconverter upconvertReply>
StatusWith<RemoteCommandResponse> runDownconvertedCommand(DBClientConnection* conn,
@@ -104,7 +104,7 @@ StatusWith<RemoteCommandResponse> runDownconvertedCommand(DBClientConnection* co
auto messageId = requestMsg.header().getId();
- return upconvertReply(messageId, DbMessage(requestMsg).getns(), responseMsg);
+ return upconvertReply(messageId, DbMessage(requestMsg).getns(), &responseMsg);
}
/**
@@ -114,7 +114,7 @@ StatusWith<RemoteCommandResponse> runDownconvertedCommand(DBClientConnection* co
StatusWith<RemoteCommandResponse> runDownconvertedFindCommand(DBClientConnection* conn,
const RemoteCommandRequest& request) {
return runDownconvertedCommand<executor::downconvertFindCommandRequest,
- executor::upconvertLegacyQueryResponse>(conn, request);
+ executor::prepareOpReplyErrorResponse>(conn, request);
}
/**
@@ -124,7 +124,7 @@ StatusWith<RemoteCommandResponse> runDownconvertedFindCommand(DBClientConnection
StatusWith<RemoteCommandResponse> runDownconvertedGetMoreCommand(
DBClientConnection* conn, const RemoteCommandRequest& request) {
return runDownconvertedCommand<executor::downconvertGetMoreCommandRequest,
- executor::upconvertLegacyGetMoreResponse>(conn, request);
+ executor::prepareOpReplyErrorResponse>(conn, request);
}
} // namespace
diff --git a/src/mongo/executor/downconvert_find_and_getmore_commands.cpp b/src/mongo/executor/downconvert_find_and_getmore_commands.cpp
index 034c1f133cc..799de9ca11f 100644
--- a/src/mongo/executor/downconvert_find_and_getmore_commands.cpp
+++ b/src/mongo/executor/downconvert_find_and_getmore_commands.cpp
@@ -34,8 +34,6 @@
#include <string>
#include <tuple>
-#include "mongo/base/data_range_cursor.h"
-#include "mongo/base/data_type_validated.h"
#include "mongo/base/status_with.h"
#include "mongo/client/constants.h"
#include "mongo/client/dbclientinterface.h"
@@ -47,7 +45,6 @@
#include "mongo/executor/remote_command_response.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/rpc/metadata/server_selection_metadata.h"
-#include "mongo/rpc/object_check.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/net/message.h"
@@ -56,75 +53,17 @@ namespace executor {
namespace {
-StatusWith<std::tuple<CursorId, BSONArray>> getBatchFromReply(std::uint32_t requestId,
- const Message& response) {
- auto header = response.header();
- if (header.getNetworkOp() != mongo::opReply) {
- return {ErrorCodes::ProtocolError,
- str::stream() << "Expected to be decoding an OP_REPLY but got "
- << mongo::networkOpToString(header.getNetworkOp())};
- }
-
- if (header.getResponseTo() != requestId) {
- return {ErrorCodes::ProtocolError,
- str::stream() << "responseTo field of OP_REPLY header with value '"
- << header.getResponseTo() << "' does not match requestId '"
- << requestId << "'"};
- }
-
- if ((header.dataLen() < 0) ||
- (static_cast<std::size_t>(header.dataLen()) > mongo::MaxMessageSizeBytes)) {
- return {ErrorCodes::InvalidLength,
- str::stream() << "Received message has invalid length field with value "
- << header.dataLen()};
- }
-
- QueryResult::View qr = response.header().view2ptr();
-
- auto resultFlags = qr.getResultFlags();
-
- if (resultFlags & ResultFlag_CursorNotFound) {
- return {ErrorCodes::CursorNotFound,
- str::stream() << "Cursor with id '" << qr.getCursorId() << "' not found"};
- }
-
- // Use CDRC directly instead of DocumentRange as DocumentRange has a throwing API.
- ConstDataRangeCursor cdrc{qr.data(), qr.data() + header.dataLen()};
-
- if (resultFlags & ResultFlag_ErrSet) {
- if (qr.getNReturned() != 1) {
- return {ErrorCodes::BadValue,
- str::stream() << "ResultFlag_ErrSet flag set on reply, but nReturned was '"
- << qr.getNReturned() << "' - expected 1"};
- }
- // Convert error document to a Status.
- // Will throw if first document is invalid BSON.
- auto first = cdrc.readAndAdvance<Validated<BSONObj>>();
- if (!first.isOK()) {
- return first.getStatus();
- }
-
- // Convert error document to a status.
- return getStatusFromCommandResult(first.getValue());
- }
-
- Validated<BSONObj> nextObj;
- BSONArrayBuilder batch;
- while (!cdrc.empty() && batch.arrSize() < qr.getNReturned()) {
- auto readStatus = cdrc.readAndAdvance(&nextObj);
- if (!readStatus.isOK()) {
- return readStatus;
- }
- batch.append(nextObj.val);
- }
- if (qr.getNReturned() != batch.arrSize()) {
- return {ErrorCodes::InvalidLength,
- str::stream() << "Count of documents in OP_REPLY message (" << batch.arrSize()
- << ") did not match the value specified in the nReturned field ("
- << qr.getNReturned() << ")"};
- }
-
- return {std::make_tuple(qr.getCursorId(), batch.arr())};
+/**
+ * Returns a non-OK status if 'response' does not report it is a response to the request with id
+ * 'requestId'.
+ */
+Status checkMessageResponseTo(std::uint32_t requestId, const Message* response) {
+ return response->header().getResponseTo() == requestId
+ ? Status::OK()
+ : Status{ErrorCodes::ProtocolError,
+ str::stream() << "responseTo field of OP_REPLY header with value "
+ << response->header().getResponseTo() << " does not match requestId "
+ << requestId};
}
} // namespace
@@ -204,28 +143,21 @@ StatusWith<Message> downconvertFindCommandRequest(const RemoteCommandRequest& re
return {std::move(message)};
}
-StatusWith<RemoteCommandResponse> upconvertLegacyQueryResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response) {
- auto swBatch = getBatchFromReply(requestId, response);
- if (!swBatch.isOK()) {
- return swBatch.getStatus();
+StatusWith<RemoteCommandResponse> prepareOpReplyErrorResponse(std::uint32_t requestId,
+ StringData cursorNamespace,
+ Message* response) {
+ auto status = checkMessageResponseTo(requestId, response);
+ if (!status.isOK()) {
+ return status;
}
- BSONArray batch;
- CursorId cursorId;
- std::tie(cursorId, batch) = std::move(swBatch.getValue());
-
BSONObjBuilder result;
- appendCursorResponseObject(cursorId, cursorNamespace, std::move(batch), &result);
- // Using Command::appendCommandStatus would create a circular dep, so it's simpler to just do
- // this.
- result.append("ok", 1.0);
-
- RemoteCommandResponse upconvertedResponse;
- upconvertedResponse.data = result.obj();
+ result.append("ok", 0.0);
+ result.append("code", ErrorCodes::ReceivedOpReplyMessage);
+ result.append("errmsg", "Received an OP_REPLY, unable to parse into a single BSONObj");
+ result.append("ns", cursorNamespace);
- return {std::move(upconvertedResponse)};
+ return {RemoteCommandResponse{std::move(*response), result.obj(), {}, {}}};
}
StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest& request) {
@@ -248,28 +180,5 @@ StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest&
return {std::move(m)};
}
-StatusWith<RemoteCommandResponse> upconvertLegacyGetMoreResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response) {
- auto swBatch = getBatchFromReply(requestId, response);
- if (!swBatch.isOK()) {
- return swBatch.getStatus();
- }
-
- BSONArray batch;
- CursorId cursorId;
-
- std::tie(cursorId, batch) = std::move(swBatch.getValue());
-
- BSONObjBuilder result;
- appendGetMoreResponseObject(cursorId, cursorNamespace, std::move(batch), &result);
- result.append("ok", 1.0);
-
- RemoteCommandResponse resp;
- resp.data = result.obj();
-
- return {std::move(resp)};
-}
-
} // namespace mongo
} // namespace executor
diff --git a/src/mongo/executor/downconvert_find_and_getmore_commands.h b/src/mongo/executor/downconvert_find_and_getmore_commands.h
index dea13150ddd..6dd21318e76 100644
--- a/src/mongo/executor/downconvert_find_and_getmore_commands.h
+++ b/src/mongo/executor/downconvert_find_and_getmore_commands.h
@@ -29,6 +29,9 @@
#include <cstdint>
#include <memory>
+#include "mongo/db/cursor_id.h"
+#include "mongo/util/time_support.h"
+
namespace mongo {
class Message;
@@ -52,13 +55,14 @@ struct RemoteCommandResponse;
StatusWith<Message> downconvertFindCommandRequest(const RemoteCommandRequest& request);
/**
- * Upconverts the OP_REPLY received in response to a legacy OP_QUERY to a semantically equivalent
- * find command response. The 'requestId' parameter is the messageId of the original OP_QUERY, and
- * the 'cursorNamespace' is the full namespace of the collection the query ran on.
+ * We may not be able to fit the entire batch from the OP_REPLY into a single response BSONObj, so
+ * we must defer the parsing to the original requester. This method creates a RemoteCommandResponse
+ * with a response that will signal callers to decode the raw message. The RemoteCommandResponse
+ * returned by this method takes ownership of 'message'.
*/
-StatusWith<RemoteCommandResponse> upconvertLegacyQueryResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response);
+StatusWith<RemoteCommandResponse> prepareOpReplyErrorResponse(std::uint32_t requestId,
+ StringData cursorNamespace,
+ Message* response);
/**
* Downconverts a getMore command request to the legacy OP_GET_MORE format. The returned message
@@ -68,14 +72,5 @@ StatusWith<RemoteCommandResponse> upconvertLegacyQueryResponse(std::uint32_t req
*/
StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest& request);
-/**
- * Upconverts the OP_REPLY received in response to a legacy OP_GET_MORE to a semantically equivalent
- * getMore command response. The 'requestId' parameter is the messageId of the original OP_GET_MORE,
- * and the 'curesorNamespace' is the full namespace of the collection the original query ran on.
- */
-StatusWith<RemoteCommandResponse> upconvertLegacyGetMoreResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response);
-
} // namespace mongo
} // namespace executor
diff --git a/src/mongo/executor/network_interface_asio_command.cpp b/src/mongo/executor/network_interface_asio_command.cpp
index 90fde01055d..88daf7aafe4 100644
--- a/src/mongo/executor/network_interface_asio_command.cpp
+++ b/src/mongo/executor/network_interface_asio_command.cpp
@@ -205,13 +205,10 @@ ResponseStatus NetworkInterfaceASIO::AsyncCommand::response(rpc::Protocol protoc
case CommandType::kRPC: {
return decodeRPC(&received, protocol, now - _start, _target, metadataHook);
}
- case CommandType::kDownConvertedFind: {
- auto ns = DbMessage(_toSend).getns();
- return upconvertLegacyQueryResponse(_toSend.header().getId(), ns, received);
- }
+ case CommandType::kDownConvertedFind:
case CommandType::kDownConvertedGetMore: {
auto ns = DbMessage(_toSend).getns();
- return upconvertLegacyGetMoreResponse(_toSend.header().getId(), ns, received);
+ return prepareOpReplyErrorResponse(_toSend.header().getId(), ns, &received);
}
}
MONGO_UNREACHABLE;