void killRemoteCursor(OperationContext* opCtx, executor::TaskExecutor* executor, RemoteCursor&& cursor, const NamespaceString& nss) { BSONObj cmdObj = KillCursorsRequest(nss, {cursor.getCursorResponse().getCursorId()}).toBSON(); executor::RemoteCommandRequest request( cursor.getHostAndPort(), nss.db().toString(), cmdObj, opCtx); // We do not process the response to the killCursors request (we make a good-faith // attempt at cleaning up the cursors, but ignore any returned errors). executor->scheduleRemoteCommand(request, [](auto const&) {}).getStatus().ignore(); }
void AsyncResultsMerger::scheduleKillCursors_inlock() { invariant(_lifecycleState == kKillStarted); invariant(_killCursorsScheduledEvent.isValid()); for (const auto& remote : _remotes) { invariant(!remote.cbHandle.isValid()); if (remote.status.isOK() && remote.cursorId && !remote.exhausted()) { BSONObj cmdObj = KillCursorsRequest(_params.nsString, {*remote.cursorId}).toBSON(); executor::RemoteCommandRequest request( remote.getTargetHost(), _params.nsString.db().toString(), cmdObj, _params.txn); _executor->scheduleRemoteCommand( request, stdx::bind(&AsyncResultsMerger::handleKillCursorsResponse, stdx::placeholders::_1)); } } }
StatusWith<KillCursorsRequest> KillCursorsRequest::parseFromBSON(const std::string& dbname, const BSONObj& cmdObj) { if (!str::equals(cmdObj.firstElement().fieldName(), kCmdName)) { return {ErrorCodes::FailedToParse, str::stream() << "First field name must be '" << kCmdName << "' in: " << cmdObj}; } if (cmdObj.firstElement().type() != BSONType::String) { return {ErrorCodes::FailedToParse, str::stream() << "First parameter must be a string in: " << cmdObj}; } std::string collName = cmdObj.firstElement().String(); const NamespaceString nss(dbname, collName); if (!nss.isValid()) { return {ErrorCodes::InvalidNamespace, str::stream() << "Invalid collection name: " << nss.ns()}; } if (cmdObj[kCursorsField].type() != BSONType::Array) { return {ErrorCodes::FailedToParse, str::stream() << "Field '" << kCursorsField << "' must be of type array in: " << cmdObj}; } std::vector<CursorId> cursorIds; for (BSONElement cursorEl : cmdObj[kCursorsField].Obj()) { if (cursorEl.type() != BSONType::NumberLong) { return {ErrorCodes::FailedToParse, str::stream() << "Field '" << kCursorsField << "' contains an element that is not of type long: " << cursorEl}; } cursorIds.push_back(cursorEl.numberLong()); } if (cursorIds.empty()) { return {ErrorCodes::BadValue, str::stream() << "Must specify at least one cursor id in: " << cmdObj}; } return KillCursorsRequest(nss, cursorIds); }
StatusWith<std::vector<ClusterClientCursorParams::RemoteCursor>> establishCursors( OperationContext* opCtx, executor::TaskExecutor* executor, const NamespaceString& nss, const ReadPreferenceSetting readPref, const std::vector<std::pair<ShardId, BSONObj>>& remotes, bool allowPartialResults, BSONObj* viewDefinition) { // Construct the requests std::vector<AsyncRequestsSender::Request> requests; for (const auto& remote : remotes) { requests.emplace_back(remote.first, remote.second); } // Send the requests AsyncRequestsSender ars(opCtx, executor, nss.db().toString(), std::move(requests), readPref, Shard::RetryPolicy::kIdempotent); // Get the responses std::vector<ClusterClientCursorParams::RemoteCursor> remoteCursors; Status status = Status::OK(); while (!ars.done()) { auto response = ars.next(); StatusWith<CursorResponse> swCursorResponse( response.swResponse.isOK() ? CursorResponse::parseFromBSON(response.swResponse.getValue().data) : response.swResponse.getStatus()); if (swCursorResponse.isOK()) { remoteCursors.emplace_back(std::move(response.shardId), std::move(*response.shardHostAndPort), std::move(swCursorResponse.getValue())); continue; } // In the case a read is performed against a view, the shard primary can return an error // indicating that the underlying collection may be sharded. When this occurs the return // message will include an expanded view definition and collection namespace which we // need to store. This allows for a second attempt at the read directly against the // underlying collection. if (swCursorResponse.getStatus() == ErrorCodes::CommandOnShardedViewNotSupportedOnMongod) { auto& responseObj = response.swResponse.getValue().data; if (!responseObj.hasField("resolvedView")) { status = Status(ErrorCodes::InternalError, str::stream() << "Missing field 'resolvedView' in document: " << responseObj); break; } auto resolvedViewObj = responseObj.getObjectField("resolvedView"); if (resolvedViewObj.isEmpty()) { status = Status(ErrorCodes::InternalError, str::stream() << "Field 'resolvedView' must be an object: " << responseObj); break; } status = std::move(swCursorResponse.getStatus()); if (viewDefinition) { *viewDefinition = BSON("resolvedView" << resolvedViewObj.getOwned()); } break; } // Unreachable host errors are swallowed if the 'allowPartialResults' option is set. if (allowPartialResults) { continue; } status = std::move(swCursorResponse.getStatus()); break; } // 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. if (!status.isOK()) { // 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()) { remoteCursors.emplace_back(std::move(response.shardId), *response.shardHostAndPort, std::move(swCursorResponse.getValue())); } } // Schedule killCursors against all cursors that were established. for (const auto& remoteCursor : remoteCursors) { BSONObj cmdObj = KillCursorsRequest(nss, {remoteCursor.cursorResponse.getCursorId()}).toBSON(); executor::RemoteCommandRequest request( remoteCursor.hostAndPort, nss.db().toString(), cmdObj, opCtx); // We do not process the response to the killCursors request (we make a good-faith // attempt at cleaning up the cursors, but ignore any returned errors). executor ->scheduleRemoteCommand( request, [](const executor::TaskExecutor::RemoteCommandCallbackArgs& cbData) {}) .status_with_transitional_ignore(); } return status; } return std::move(remoteCursors); }