/// <summary> /// Executes a <see cref="ISearchQuery" /> request including any <see cref="ISearchOptions" /> parameters asynchronously. /// </summary> /// <returns>A <see cref="ISearchResult"/> wrapped in a <see cref="Task"/> for awaiting on.</returns> public async Task <ISearchResult> QueryAsync(SearchRequest searchRequest, CancellationToken cancellationToken = default) { // try get Search node var searchUri = _serviceUriProvider.GetRandomSearchUri(); var uriBuilder = new UriBuilder(searchUri) { Path = $"api/index/{searchRequest.Index}/query" }; var searchResult = new SearchResult(); var searchBody = searchRequest.ToJson(); try { using var content = new StringContent(searchBody, Encoding.UTF8, MediaType.Json); var response = await HttpClient.PostAsync(uriBuilder.Uri, content, cancellationToken).ConfigureAwait(false); using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { searchResult = await _dataMapper.MapAsync <SearchResult>(stream, cancellationToken).ConfigureAwait(false); } else { using var reader = new StreamReader(stream); var errorResult = await reader.ReadToEndAsync().ConfigureAwait(false); } } searchResult.HttpStatusCode = response.StatusCode; if (searchResult.ShouldRetry()) { UpdateLastActivity(); return(searchResult); } } catch (OperationCanceledException e) { Log.LogDebug(LoggingEvents.SearchEvent, e, "Search request timeout."); throw new AmbiguousTimeoutException("The query was timed out via the Token.", e); } catch (HttpRequestException e) { Log.LogDebug(LoggingEvents.SearchEvent, e, "Search request cancelled."); throw new RequestCanceledException("The query was canceled.", e); } UpdateLastActivity(); return(searchResult); }
private Uri GetIndexUri(string?indexName = null) { var searchUri = _serviceUriProvider.GetRandomSearchUri(); var builder = new UriBuilder(searchUri) { Path = "api/index" }; if (!string.IsNullOrWhiteSpace(indexName)) { builder.Path += $"/{indexName}"; } return(builder.Uri); }
public async Task <ISearchResult> QueryAsync(SearchRequest searchRequest, CancellationToken cancellationToken = default) { using var rootSpan = RootSpan(OuterRequestSpans.ServiceSpan.SearchQuery) .WithLocalAddress(); using var encodingSpan = rootSpan.EncodingSpan(); // try get Search nodes var searchUri = _serviceUriProvider.GetRandomSearchUri(); rootSpan.WithRemoteAddress(searchUri); var uriBuilder = new UriBuilder(searchUri) { Path = $"api/index/{searchRequest.Index}/query" }; _logger.LogDebug("Sending FTS query with a context id {contextId} to server {searchUri}", searchRequest.ClientContextId, searchUri); var searchResult = new SearchResult(); var searchBody = searchRequest.ToJson(); string?errors = null; try { using var content = new StringContent(searchBody, Encoding.UTF8, MediaType.Json); encodingSpan.Dispose(); using var dispatchSpan = rootSpan.DispatchSpan(searchRequest); using var httpClient = CreateHttpClient(); var response = await httpClient.PostAsync(uriBuilder.Uri, content, cancellationToken).ConfigureAwait(false); dispatchSpan.Dispose(); using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { searchResult = await _dataMapper.MapAsync <SearchResult>(stream, cancellationToken).ConfigureAwait(false); } else { using var reader = new StreamReader(stream); errors = await reader.ReadToEndAsync().ConfigureAwait(false); var json = JsonConvert.DeserializeObject <JObject>(errors); var queryError = json?.SelectToken("error"); //If the query service returned a top level error then //use it otherwise the error is in the response body if (queryError != null) { errors = queryError.Value <string>() ?? ""; } var ctx = new SearchErrorContext { HttpStatus = response.StatusCode, IndexName = searchRequest.Index, ClientContextId = searchRequest.ClientContextId, Statement = searchRequest.Statement, Errors = errors, Query = searchRequest.ToJson(), Message = errors }; //Rate limiting errors if (response.StatusCode == (HttpStatusCode)429) { if (errors.Contains("num_concurrent_requests")) { throw new RateLimitedException(RateLimitedReason.ConcurrentRequestLimitReached, ctx); } if (errors.Contains("num_queries_per_min")) { throw new RateLimitedException(RateLimitedReason.ConcurrentRequestLimitReached, ctx); } if (errors.Contains("ingress_mib_per_min")) { throw new RateLimitedException(RateLimitedReason.NetworkIngressRateLimitReached, ctx); } if (errors.Contains("egress_mib_per_min")) { throw new RateLimitedException(RateLimitedReason.NetworkEgressRateLimitReached, ctx); } } //Quota limiting errors if (response.StatusCode == HttpStatusCode.BadRequest) { if (errors.Contains("index not found")) { throw new IndexNotFoundException("The search index was not found on the server.") { Context = ctx }; } if (errors.Contains("num_fts_indexes")) { throw new QuotaLimitedException(QuotaLimitedReason.MaximumNumberOfIndexesReached, ctx); } } //Internal service errors if (response.StatusCode == HttpStatusCode.InternalServerError) { throw new InternalServerFailureException { Context = ctx }; } //Authentication errors if (response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Unauthorized) { throw new AuthenticationFailureException(ctx); } throw new CouchbaseException(errors) { Context = ctx }; } } searchResult.HttpStatusCode = response.StatusCode; if (searchResult.ShouldRetry()) { if (!response.IsSuccessStatusCode) { searchResult.NoRetryException = new CouchbaseException() { Context = new SearchErrorContext { HttpStatus = response.StatusCode, IndexName = searchRequest.Index, ClientContextId = searchRequest.ClientContextId, Statement = searchRequest.Statement, Errors = errors, Query = searchRequest.ToJson() } }; } UpdateLastActivity(); return(searchResult); } } catch (OperationCanceledException e) { //treat as an orphaned response rootSpan.LogOrphaned(); _logger.LogDebug(LoggingEvents.SearchEvent, e, "Search request timeout."); throw new AmbiguousTimeoutException("The query was timed out via the Token.", e) { Context = new SearchErrorContext { HttpStatus = HttpStatusCode.RequestTimeout, IndexName = searchRequest.Index, ClientContextId = searchRequest.ClientContextId, Statement = searchRequest.Statement, Errors = errors, Query = searchRequest.ToJson() } }; } catch (HttpRequestException e) { //treat as an orphaned response rootSpan.LogOrphaned(); _logger.LogDebug(LoggingEvents.SearchEvent, e, "Search request cancelled."); throw new RequestCanceledException("The query was canceled.", e) { Context = new SearchErrorContext { HttpStatus = HttpStatusCode.RequestTimeout, IndexName = searchRequest.Index, ClientContextId = searchRequest.ClientContextId, Statement = searchRequest.Statement, Errors = errors, Query = searchRequest.ToJson() } }; } UpdateLastActivity(); return(searchResult); }
public async Task <ISearchResult> QueryAsync(SearchRequest searchRequest, CancellationToken cancellationToken = default) { using var rootSpan = RootSpan(OuterRequestSpans.ServiceSpan.SearchQuery) .WithLocalAddress(); using var encodingSpan = rootSpan.EncodingSpan(); // try get Search nodes var searchUri = _serviceUriProvider.GetRandomSearchUri(); rootSpan.WithRemoteAddress(searchUri); var uriBuilder = new UriBuilder(searchUri) { Path = $"api/index/{searchRequest.Index}/query" }; _logger.LogDebug("Sending FTS query with a context id {contextId} to server {searchUri}", searchRequest.ClientContextId, searchUri); var searchResult = new SearchResult(); var searchBody = searchRequest.ToJson(); string?errors = null; try { using var content = new StringContent(searchBody, Encoding.UTF8, MediaType.Json); encodingSpan.Dispose(); using var dispatchSpan = rootSpan.DispatchSpan(searchRequest); var response = await HttpClient.PostAsync(uriBuilder.Uri, content, cancellationToken).ConfigureAwait(false); dispatchSpan.Dispose(); using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { searchResult = await _dataMapper.MapAsync <SearchResult>(stream, cancellationToken).ConfigureAwait(false); } else { using var reader = new StreamReader(stream); errors = await reader.ReadToEndAsync().ConfigureAwait(false); } } searchResult.HttpStatusCode = response.StatusCode; if (searchResult.ShouldRetry()) { UpdateLastActivity(); return(searchResult); } } catch (OperationCanceledException e) { _logger.LogDebug(LoggingEvents.SearchEvent, e, "Search request timeout."); throw new AmbiguousTimeoutException("The query was timed out via the Token.", e) { Context = new SearchErrorContext { HttpStatus = HttpStatusCode.RequestTimeout, IndexName = searchRequest.Index, ClientContextId = searchRequest.ClientContextId, Statement = searchRequest.Statement, Errors = errors, Query = searchRequest.ToJson() } }; } catch (HttpRequestException e) { _logger.LogDebug(LoggingEvents.SearchEvent, e, "Search request cancelled."); throw new RequestCanceledException("The query was canceled.", e) { Context = new SearchErrorContext { HttpStatus = HttpStatusCode.RequestTimeout, IndexName = searchRequest.Index, ClientContextId = searchRequest.ClientContextId, Statement = searchRequest.Statement, Errors = errors, Query = searchRequest.ToJson() } }; } UpdateLastActivity(); return(searchResult); }