boost::intrusive_ptr<DocumentSource> DocumentSourceMergeCursors::createFromBson(
    BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
    if (elem.type() == BSONType::Object) {
        // This is the modern serialization format. We de-serialize using the IDL.
        auto ownedObj = elem.embeddedObject().getOwned();
        auto armParams =
            AsyncResultsMergerParams::parse(IDLParserErrorContext(kStageName), ownedObj);
        return new DocumentSourceMergeCursors(
            Grid::get(expCtx->opCtx)->getExecutorPool()->getArbitraryExecutor(),
            std::move(armParams),
            expCtx,
            std::move(ownedObj));
    }

    // This is the old serialization format which can still be generated by mongos processes
    // older than 4.0.
    // TODO SERVER-34009 Remove support for this format.
    uassert(17026,
            "$mergeCursors stage expected either an array or an object as argument",
            elem.type() == BSONType::Array);
    const auto serializedRemotes = elem.Array();
    uassert(50729,
            "$mergeCursors stage expected array with at least one entry",
            serializedRemotes.size() > 0);

    boost::optional<NamespaceString> nss;
    std::vector<RemoteCursor> remotes;
    for (auto&& cursor : serializedRemotes) {
        BSONElement nsElem;
        BSONElement hostElem;
        BSONElement idElem;
        uassert(17027,
                "$mergeCursors stage requires each cursor in array to be an object",
                cursor.type() == BSONType::Object);
        for (auto&& cursorElem : cursor.Obj()) {
            const auto fieldName = cursorElem.fieldNameStringData();
            if (fieldName == "ns"_sd) {
                nsElem = cursorElem;
            } else if (fieldName == "host"_sd) {
                hostElem = cursorElem;
            } else if (fieldName == "id"_sd) {
                idElem = cursorElem;
            } else {
                uasserted(50730,
                          str::stream() << "Unrecognized option " << fieldName
                                        << " within cursor provided to $mergeCursors: "
                                        << cursor);
            }
        }
        uassert(
            50731,
            "$mergeCursors stage requires \'ns\' field with type string for each cursor in array",
            nsElem.type() == BSONType::String);

        // We require each cursor to have the same namespace. This isn't a fundamental limit of the
        // system, but needs to be true due to the implementation of AsyncResultsMerger, which
        // tracks one namespace for all cursors.
        uassert(50720,
                "$mergeCursors requires each cursor to have the same namespace",
                !nss || nss->ns() == nsElem.valueStringData());
        nss = NamespaceString(nsElem.String());

        uassert(
            50721,
            "$mergeCursors stage requires \'host\' field with type string for each cursor in array",
            hostElem.type() == BSONType::String);
        auto host = uassertStatusOK(HostAndPort::parse(hostElem.valueStringData()));

        uassert(50722,
                "$mergeCursors stage requires \'id\' field with type long for each cursor in array",
                idElem.type() == BSONType::NumberLong);
        auto cursorId = idElem.Long();

        // We are assuming that none of the cursors have been iterated at all, and so will not have
        // any data in the initial batch.
        // TODO SERVER-33323 We use a fake shard id because the AsyncResultsMerger won't use it for
        // anything, and finding the real one is non-trivial.
        RemoteCursor remoteCursor;
        remoteCursor.setShardId(ShardId("fakeShardIdForMergeCursors"));
        remoteCursor.setHostAndPort(std::move(host));
        std::vector<BSONObj> emptyBatch;
        remoteCursor.setCursorResponse(CursorResponse{*nss, cursorId, emptyBatch});
        remotes.push_back(std::move(remoteCursor));
    }
    invariant(nss);  // We know there is at least one cursor in 'serializedRemotes', and we require
                     // each cursor to have a 'ns' field.

    AsyncResultsMergerParams params;
    params.setRemotes(std::move(remotes));
    params.setNss(*nss);
    return new DocumentSourceMergeCursors(
        Grid::get(expCtx->opCtx)->getExecutorPool()->getArbitraryExecutor(),
        std::move(params),
        expCtx,
        elem.embeddedObject().getOwned());
}
std::vector<RemoteCursor> establishCursors(OperationContext* opCtx,
                                           executor::TaskExecutor* executor,
                                           const NamespaceString& nss,
                                           const ReadPreferenceSetting readPref,
                                           const std::vector<std::pair<ShardId, BSONObj>>& remotes,
                                           bool allowPartialResults,
                                           Shard::RetryPolicy retryPolicy) {
    // Construct the requests
    std::vector<AsyncRequestsSender::Request> requests;
    for (const auto& remote : remotes) {
        requests.emplace_back(remote.first, remote.second);
    }

    // Send the requests
    MultiStatementTransactionRequestsSender ars(
        opCtx, executor, nss.db().toString(), std::move(requests), readPref, retryPolicy);

    std::vector<RemoteCursor> remoteCursors;
    try {
        // Get the responses
        while (!ars.done()) {
            try {
                auto response = ars.next();
                // Note the shardHostAndPort may not be populated if there was an error, so be sure
                // to do this after parsing the cursor response to ensure the response was ok.
                // Additionally, be careful not to push into 'remoteCursors' until we are sure we
                // have a valid cursor, since the error handling path will attempt to clean up
                // anything in 'remoteCursors'
                auto cursors = CursorResponse::parseFromBSONMany(
                    uassertStatusOK(std::move(response.swResponse)).data);

                for (auto& cursor : cursors) {
                    if (cursor.isOK()) {
                        RemoteCursor remoteCursor;
                        remoteCursor.setCursorResponse(std::move(cursor.getValue()));
                        remoteCursor.setShardId(std::move(response.shardId));
                        remoteCursor.setHostAndPort(*response.shardHostAndPort);
                        remoteCursors.push_back(std::move(remoteCursor));
                    }
                }

                // Throw if there is any error and then the catch block below will do the cleanup.
                for (auto& cursor : cursors) {
                    uassertStatusOK(cursor.getStatus());
                }

            } catch (const DBException& ex) {
                // Retriable errors are swallowed if 'allowPartialResults' is true.
                if (allowPartialResults &&
                    std::find(RemoteCommandRetryScheduler::kAllRetriableErrors.begin(),
                              RemoteCommandRetryScheduler::kAllRetriableErrors.end(),
                              ex.code()) !=
                        RemoteCommandRetryScheduler::kAllRetriableErrors.end()) {
                    continue;
                }
                throw;  // Fail this loop.
            }
        }
        return remoteCursors;
    } catch (const DBException&) {
        // If one of the remotes had an error, we make a best effort to finish retrieving responses
        // for other requests that were already sent, so that we can send killCursors to any cursors
        // that we know were established.
        try {
            // Do not schedule any new requests.
            ars.stopRetrying();

            // Collect responses from all requests that were already sent.
            while (!ars.done()) {
                auto response = ars.next();

                // Check if the response contains an established cursor, and if so, store it.
                StatusWith<CursorResponse> swCursorResponse(
                    response.swResponse.isOK()
                        ? CursorResponse::parseFromBSON(response.swResponse.getValue().data)
                        : response.swResponse.getStatus());

                if (swCursorResponse.isOK()) {
                    RemoteCursor cursor;
                    cursor.setShardId(std::move(response.shardId));
                    cursor.setHostAndPort(*response.shardHostAndPort);
                    cursor.setCursorResponse(std::move(swCursorResponse.getValue()));
                    remoteCursors.push_back(std::move(cursor));
                }
            }

            // Schedule killCursors against all cursors that were established.
            killRemoteCursors(opCtx, executor, std::move(remoteCursors), nss);
        } catch (const DBException&) {
            // Ignore the new error and rethrow the original one.
        }

        throw;
    }
}