diff options
author | Charlie Swanson <charlie.swanson@mongodb.com> | 2016-11-02 18:08:23 -0400 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2016-11-08 10:16:56 -0500 |
commit | 524cc9989bcf743b34bcc9e9d05d9f72cae48620 (patch) | |
tree | a17f8144564b141ba140dd6494d1260edca21d44 | |
parent | 7c626977cf052314d09648d1061e07c44e964264 (diff) | |
download | mongo-524cc9989bcf743b34bcc9e9d05d9f72cae48620.tar.gz |
SERVER-26182 Avoid putting OP_REPLY batches >16MB into a single BSONObj
-rw-r--r-- | jstests/multiVersion/large_document_sync.js | 62 | ||||
-rw-r--r-- | src/mongo/base/error_codes.err | 1 | ||||
-rw-r--r-- | src/mongo/client/fetcher.cpp | 173 | ||||
-rw-r--r-- | src/mongo/client/remote_command_runner_impl.cpp | 8 | ||||
-rw-r--r-- | src/mongo/executor/downconvert_find_and_getmore_commands.cpp | 135 | ||||
-rw-r--r-- | src/mongo/executor/downconvert_find_and_getmore_commands.h | 25 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_command.cpp | 7 |
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; |