/// <inheritdoc />
        public Task <HttpResponseMessage> DeleteAsync(string path, IRequestSpan parentSpan, IRequestSpan encodeSpan, CancellationToken token)
        {
            var requestUri = GetUri(path);

            parentSpan.WithRemoteAddress(requestUri);

            encodeSpan.Dispose();
            using var dispatchSpan = parentSpan.DispatchSpan();
            return(HttpClient.DeleteAsync(requestUri, token));
        }
        /// <inheritdoc />
        public Task <HttpResponseMessage> PostAsync(string path, IRequestSpan parentSpan, IRequestSpan encodeSpan, CancellationToken token, EventingFunction eventingFunction = null)
        {
            var requestUri = GetUri(path);

            parentSpan.WithRemoteAddress(requestUri);

            var content = eventingFunction != null ?
                          new StringContent(eventingFunction.ToJson()) :
                          new StringContent(string.Empty);

            encodeSpan.Dispose();
            using var dispatchSpan = parentSpan.DispatchSpan();
            return(HttpClient.PostAsync(requestUri, content, token));
        }
Example #3
0
        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
            var queryUri = _serviceUriProvider.GetRandomQueryUri();

            span.WithRemoteAddress(queryUri);
            using var encodingSpan = span.EncodingSpan();
            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.DispatchSpan(options);
                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))
                    {
                        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);
                }
            }
            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);
        }