private async Task <IQueryResult <T> > ExecuteQuery <T>(QueryOptions options, ITypeSerializer serializer, IInternalSpan span) { // try get Query node var queryUri = _serviceUriProvider.GetRandomQueryUri(); span.WithRemoteAddress(queryUri); using var encodingSpan = span.StartPayloadEncoding(); var body = options.GetFormValuesAsJson(); encodingSpan.Dispose(); _logger.LogDebug("Sending query {contextId} to node {endpoint}.", options.CurrentContextId, queryUri); QueryResultBase <T> queryResult; using var content = new StringContent(body, System.Text.Encoding.UTF8, MediaType.Json); try { using var dispatchSpan = span.StartDispatch(); var response = await HttpClient.PostAsync(queryUri, content, options.Token).ConfigureAwait(false); dispatchSpan.Dispose(); var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); if (serializer is IStreamingTypeDeserializer streamingDeserializer) { queryResult = new StreamingQueryResult <T>(stream, streamingDeserializer); } else { queryResult = new BlockQueryResult <T>(stream, serializer); } queryResult.HttpStatusCode = response.StatusCode; queryResult.Success = response.StatusCode == HttpStatusCode.OK; //read the header and stop when we reach the queried rows await queryResult.InitializeAsync(options.Token).ConfigureAwait(false); if (response.StatusCode != HttpStatusCode.OK || queryResult.MetaData?.Status != QueryStatus.Success) { var currentContextId = options.CurrentContextId ?? Guid.Empty.ToString(); _logger.LogDebug("Request {currentContextId} has failed because {status}.", currentContextId, queryResult.MetaData?.Status); if (queryResult.ShouldRetry(EnhancedPreparedStatementsEnabled)) { if (queryResult.Errors.Any(x => x.Code == 4040 && EnhancedPreparedStatementsEnabled)) { //clear the cache of stale query plan var statement = options.StatementValue ?? string.Empty; if (_queryCache.TryRemove(statement, out var queryPlan)) { _logger.LogDebug("Query plan is stale for {currentContextId}. Purging plan {queryPlanName}.", currentContextId, queryPlan.Name); } ; } _logger.LogDebug("Request {currentContextId} is being retried.", currentContextId); return(queryResult); } var context = new QueryErrorContext { ClientContextId = options.CurrentContextId, Parameters = options.GetAllParametersAsJson(), Statement = options.ToString(), Message = GetErrorMessage(queryResult, currentContextId, response.StatusCode), Errors = queryResult.Errors, HttpStatus = response.StatusCode, QueryStatus = queryResult.MetaData?.Status ?? QueryStatus.Fatal }; if (queryResult.MetaData?.Status == QueryStatus.Timeout) { if (options.IsReadOnly) { throw new AmbiguousTimeoutException { Context = context }; } throw new UnambiguousTimeoutException { Context = context }; } queryResult.ThrowExceptionOnError(context); } } catch (OperationCanceledException e) { var context = new QueryErrorContext { ClientContextId = options.CurrentContextId, Parameters = options.GetAllParametersAsJson(), Statement = options.ToString(), HttpStatus = HttpStatusCode.RequestTimeout, QueryStatus = QueryStatus.Fatal }; _logger.LogDebug(LoggingEvents.QueryEvent, e, "Request timeout."); if (options.IsReadOnly) { throw new UnambiguousTimeoutException("The query was timed out via the Token.", e) { Context = context }; } throw new AmbiguousTimeoutException("The query was timed out via the Token.", e) { Context = context }; } catch (HttpRequestException e) { _logger.LogDebug(LoggingEvents.QueryEvent, e, "Request canceled"); var context = new QueryErrorContext { ClientContextId = options.CurrentContextId, Parameters = options.GetAllParametersAsJson(), Statement = options.ToString(), HttpStatus = HttpStatusCode.RequestTimeout, QueryStatus = QueryStatus.Fatal }; throw new RequestCanceledException("The query was canceled.", e) { Context = context }; } _logger.LogDebug($"Request {options.CurrentContextId} has succeeded."); return(queryResult); }
private async Task <IQueryResult <T> > ExecuteQuery <T>(QueryOptions options, IDataMapper dataMapper) { // try get Query node var queryUri = _serviceUriProvider.GetRandomQueryUri(); var body = options.GetFormValuesAsJson(); QueryResultBase <T> queryResult; using (var content = new StringContent(body, System.Text.Encoding.UTF8, MediaType.Json)) { try { var response = await HttpClient.PostAsync(queryUri, content, options.Token).ConfigureAwait(false); var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); if (_serializer is IStreamingTypeDeserializer streamingDeserializer) { queryResult = new StreamingQueryResult <T>(stream, streamingDeserializer); } else { queryResult = new BlockQueryResult <T>(stream, _serializer); } queryResult.HttpStatusCode = response.StatusCode; queryResult.Success = response.StatusCode == HttpStatusCode.OK; //read the header and stop when we reach the queried rows await queryResult.InitializeAsync(options.Token).ConfigureAwait(false); if (response.StatusCode != HttpStatusCode.OK || queryResult.MetaData.Status != QueryStatus.Success) { Log.LogDebug($"Request {options.CurrentContextId} has failed because {queryResult.MetaData.Status}."); if (queryResult.ShouldRetry()) { return(queryResult); } var context = new QueryErrorContext { Message = queryResult.Message, Errors = queryResult.Errors, HttpStatus = response.StatusCode, QueryStatus = queryResult.MetaData.Status }; if (queryResult.MetaData.Status == QueryStatus.Timeout) { if (options.IsReadOnly) { throw new AmbiguousTimeoutException { Context = context }; } throw new UnambiguousTimeoutException { Context = context }; } queryResult.ThrowExceptionOnError(context); } } catch (OperationCanceledException e) { Log.LogDebug(LoggingEvents.QueryEvent, e, "Request timeout."); if (options.IsReadOnly) { throw new UnambiguousTimeoutException("The query was timed out via the Token.", e); } throw new AmbiguousTimeoutException("The query was timed out via the Token.", e); } catch (HttpRequestException e) { Log.LogDebug(LoggingEvents.QueryEvent, e, "Request canceled"); throw new RequestCanceledException("The query was canceled.", e); } } Log.LogDebug($"Request {options.CurrentContextId} has succeeded."); return(queryResult); }
private async Task <IQueryResult <T> > ExecuteQuery <T>(QueryOptions options, ITypeSerializer serializer, IRequestSpan span) { var currentContextId = options.CurrentContextId ?? DefaultClientContextId; QueryErrorContext ErrorContextFactory(QueryResultBase <T> failedQueryResult, HttpStatusCode statusCode) { // We use a local function to capture context like options and currentContextId return(new() { ClientContextId = options.CurrentContextId, Parameters = options.GetAllParametersAsJson(), Statement = options.ToString(), Message = GetErrorMessage(failedQueryResult, currentContextId, statusCode), Errors = failedQueryResult.Errors, HttpStatus = statusCode, QueryStatus = failedQueryResult.MetaData?.Status ?? QueryStatus.Fatal }); } // try get Query node Uri queryUri = options.LastDispatchedNode ?? _serviceUriProvider.GetRandomQueryUri(); span.WithRemoteAddress(queryUri); using var encodingSpan = span.EncodingSpan(); using var content = options.GetRequestBody(serializer); encodingSpan.Dispose(); _logger.LogDebug("Sending query {contextId} to node {endpoint}.", options.CurrentContextId, queryUri); QueryResultBase <T> queryResult; try { using var dispatchSpan = span.DispatchSpan(options); using var httpClient = CreateHttpClient(options.TimeoutValue); var response = await httpClient.PostAsync(queryUri, content, options.Token).ConfigureAwait(false); dispatchSpan.Dispose(); var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); if (serializer is IStreamingTypeDeserializer streamingDeserializer) { queryResult = new StreamingQueryResult <T>(stream, streamingDeserializer, ErrorContextFactory); } else { queryResult = new BlockQueryResult <T>(stream, serializer); } queryResult.HttpStatusCode = response.StatusCode; queryResult.Success = response.StatusCode == HttpStatusCode.OK; //read the header and stop when we reach the queried rows await queryResult.InitializeAsync(options.Token).ConfigureAwait(false); if (response.StatusCode != HttpStatusCode.OK || queryResult.MetaData?.Status != QueryStatus.Success) { _logger.LogDebug("Request {currentContextId} has failed because {status}.", currentContextId, queryResult.MetaData?.Status); if (queryResult.ShouldRetry(EnhancedPreparedStatementsEnabled)) { queryResult.NoRetryException = queryResult.CreateExceptionForError(ErrorContextFactory(queryResult, response.StatusCode)); if (queryResult.Errors.Any(x => x.Code == 4040 && EnhancedPreparedStatementsEnabled)) { //clear the cache of stale query plan var statement = options.StatementValue ?? string.Empty; if (_queryCache.TryRemove(statement, out var queryPlan)) { _logger.LogDebug("Query plan is stale for {currentContextId}. Purging plan {queryPlanName}.", currentContextId, queryPlan.Name); } } _logger.LogDebug("Request {currentContextId} is being retried.", currentContextId); return(queryResult); } var context = ErrorContextFactory(queryResult, response.StatusCode); if (queryResult.MetaData?.Status == QueryStatus.Timeout) { if (options.IsReadOnly) { throw new AmbiguousTimeoutException { Context = context }; } throw new UnambiguousTimeoutException { Context = context }; } queryResult.ThrowExceptionOnError(context); } else if (options.StatementValue == TransactionsBeginWork) { // Internal support for Transactions query node affinity // If the result has a "txid" row, grab the value and add it to the affinity map. queryResult.MetaData.LastDispatchedToNode = queryUri; } } catch (OperationCanceledException e) { //treat as an orphaned response span.LogOrphaned(); var context = new QueryErrorContext { ClientContextId = options.CurrentContextId, Parameters = options.GetAllParametersAsJson(), Statement = options.ToString(), HttpStatus = HttpStatusCode.RequestTimeout, QueryStatus = QueryStatus.Fatal }; _logger.LogDebug(LoggingEvents.QueryEvent, e, "Request timeout."); if (options.IsReadOnly) { throw new UnambiguousTimeoutException("The query was timed out via the Token.", e) { Context = context }; } throw new AmbiguousTimeoutException("The query was timed out via the Token.", e) { Context = context }; } catch (HttpRequestException e) { _logger.LogDebug(LoggingEvents.QueryEvent, e, "Request canceled"); //treat as an orphaned response span.LogOrphaned(); var context = new QueryErrorContext { ClientContextId = options.CurrentContextId, Parameters = options.GetAllParametersAsJson(), Statement = options.ToString(), HttpStatus = HttpStatusCode.RequestTimeout, QueryStatus = QueryStatus.Fatal }; throw new RequestCanceledException("The query was canceled.", e) { Context = context }; } _logger.LogDebug($"Request {options.CurrentContextId} has succeeded."); return(queryResult); }