internal async Task <ResponseMessage> ProcessItemStreamAsync(
            PartitionKey?partitionKey,
            string itemId,
            Stream streamPayload,
            OperationType operationType,
            ItemRequestOptions requestOptions,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            if (requestOptions != null && requestOptions.IsEffectivePartitionKeyRouting)
            {
                partitionKey = null;
            }

            ContainerCore.ValidatePartitionKey(partitionKey, requestOptions);
            Uri resourceUri = this.GetResourceUri(requestOptions, operationType, itemId);

            if (diagnosticsContext == null)
            {
                diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);
            }

            using (diagnosticsContext.CreateOverallScope("ProcessItemStream"))
            {
                if (requestOptions != null && requestOptions.EncryptionOptions != null)
                {
                    if (streamPayload == null)
                    {
                        throw new ArgumentException(ClientResources.InvalidRequestWithEncryptionOptions);
                    }

                    using (diagnosticsContext.CreateScope("Encrypt"))
                    {
                        streamPayload = await this.ClientContext.EncryptionProcessor.EncryptAsync(
                            streamPayload,
                            requestOptions.EncryptionOptions,
                            (DatabaseCore)this.Database,
                            this.ClientContext.ClientOptions.EncryptionKeyWrapProvider,
                            diagnosticsContext,
                            cancellationToken);
                    }
                }

                ResponseMessage responseMessage = await this.ClientContext.ProcessResourceOperationStreamAsync(
                    resourceUri : resourceUri,
                    resourceType : ResourceType.Document,
                    operationType : operationType,
                    requestOptions : requestOptions,
                    cosmosContainerCore : this,
                    partitionKey : partitionKey,
                    itemId : itemId,
                    streamPayload : streamPayload,
                    requestEnricher : null,
                    diagnosticsScope : diagnosticsContext,
                    cancellationToken : cancellationToken);

                if (responseMessage.Content != null && this.ClientContext.ClientOptions.EncryptionKeyWrapProvider != null)
                {
                    using (diagnosticsContext.CreateScope("Decrypt"))
                    {
                        responseMessage.Content = await this.ClientContext.EncryptionProcessor.DecryptAsync(
                            responseMessage.Content,
                            (DatabaseCore)this.Database,
                            this.ClientContext.ClientOptions.EncryptionKeyWrapProvider,
                            diagnosticsContext,
                            cancellationToken);
                    }
                }

                return(responseMessage);
            }
        }
 public virtual Task <bool> ShouldRetryAsync(
     ContainerCore containerCore,
     ResponseMessage responseMessage,
     CancellationToken cancellationToken = default(CancellationToken)) => Task.FromResult(false);
Beispiel #3
0
        private static TryCatch <QueryPage> GetCosmosElementResponse(
            QueryRequestOptions requestOptions,
            ResourceType resourceType,
            ResponseMessage cosmosResponseMessage,
            ITrace trace)
        {
            using (ITrace getCosmosElementResponse = trace.StartChild("Get Cosmos Element Response", TraceComponent.Json, Tracing.TraceLevel.Info))
            {
                using (cosmosResponseMessage)
                {
                    if (cosmosResponseMessage.Headers.QueryMetricsText != null)
                    {
                        QueryMetricsTraceDatum datum = new QueryMetricsTraceDatum(
                            new Lazy <QueryMetrics>(() => new QueryMetrics(
                                                        cosmosResponseMessage.Headers.QueryMetricsText,
                                                        IndexUtilizationInfo.Empty,
                                                        ClientSideMetrics.Empty)));
                        trace.AddDatum("Query Metrics", datum);
                    }

                    if (!cosmosResponseMessage.IsSuccessStatusCode)
                    {
                        CosmosException exception = cosmosResponseMessage.CosmosException ?? new CosmosException(
                            cosmosResponseMessage.ErrorMessage,
                            cosmosResponseMessage.StatusCode,
                            (int)cosmosResponseMessage.Headers.SubStatusCode,
                            cosmosResponseMessage.Headers.ActivityId,
                            cosmosResponseMessage.Headers.RequestCharge);
                        return(TryCatch <QueryPage> .FromException(exception));
                    }

                    if (!(cosmosResponseMessage.Content is MemoryStream memoryStream))
                    {
                        memoryStream = new MemoryStream();
                        cosmosResponseMessage.Content.CopyTo(memoryStream);
                    }

                    long        responseLengthBytes = memoryStream.Length;
                    CosmosArray documents           = CosmosQueryClientCore.ParseElementsFromRestStream(
                        memoryStream,
                        resourceType,
                        requestOptions.CosmosSerializationFormatOptions);

                    QueryState queryState;
                    if (cosmosResponseMessage.Headers.ContinuationToken != null)
                    {
                        queryState = new QueryState(CosmosString.Create(cosmosResponseMessage.Headers.ContinuationToken));
                    }
                    else
                    {
                        queryState = default;
                    }

                    Dictionary <string, string> additionalHeaders = new Dictionary <string, string>();
                    foreach (string key in cosmosResponseMessage.Headers)
                    {
                        if (!QueryPage.BannedHeaders.Contains(key))
                        {
                            additionalHeaders[key] = cosmosResponseMessage.Headers[key];
                        }
                    }

                    Lazy <CosmosQueryExecutionInfo> cosmosQueryExecutionInfo = default;
                    if (cosmosResponseMessage.Headers.TryGetValue(QueryExecutionInfoHeader, out string queryExecutionInfoString))
                    {
                        cosmosQueryExecutionInfo =
                            new Lazy <CosmosQueryExecutionInfo>(
                                () => JsonConvert.DeserializeObject <CosmosQueryExecutionInfo>(queryExecutionInfoString));
                    }

                    QueryPage response = new QueryPage(
                        documents,
                        cosmosResponseMessage.Headers.RequestCharge,
                        cosmosResponseMessage.Headers.ActivityId,
                        responseLengthBytes,
                        cosmosQueryExecutionInfo,
                        disallowContinuationTokenMessage: null,
                        additionalHeaders,
                        queryState);

                    return(TryCatch <QueryPage> .FromResult(response));
                }
            }
        }
 public Task <ShouldRetryResult> ShouldRetryAsync(
     ResponseMessage cosmosResponseMessage,
     CancellationToken cancellationToken)
 {
     throw new NotImplementedException();
 }
        public override async Task <ContainerResponse> CreateContainerIfNotExistsAsync(
            ContainerProperties containerProperties,
            int?throughput = null,
            RequestOptions requestOptions       = null,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (containerProperties == null)
            {
                throw new ArgumentNullException(nameof(containerProperties));
            }

            this.ValidateContainerProperties(containerProperties);

            Container       container    = this.GetContainer(containerProperties.Id);
            ResponseMessage readResponse = await container.ReadContainerStreamAsync(
                cancellationToken : cancellationToken);

            if (readResponse.StatusCode != HttpStatusCode.NotFound)
            {
                ContainerResponse retrivedContainerResponse = await this.ClientContext.ResponseFactory.CreateContainerResponseAsync(
                    container,
                    Task.FromResult(readResponse));

                if (!retrivedContainerResponse.Resource.PartitionKeyPath.Equals(containerProperties.PartitionKeyPath))
                {
                    throw new ArgumentException(
                              string.Format(
                                  ClientResources.PartitionKeyPathConflict,
                                  containerProperties.PartitionKeyPath,
                                  containerProperties.Id,
                                  retrivedContainerResponse.Resource.PartitionKeyPath),
                              nameof(containerProperties.PartitionKey));
                }

                return(retrivedContainerResponse);
            }

            this.ValidateContainerProperties(containerProperties);
            ResponseMessage createResponse = await this.CreateContainerStreamAsync(
                containerProperties,
                throughput,
                requestOptions,
                cancellationToken);

            // Merge the previous message diagnostics
            createResponse.DiagnosticsContext.AddDiagnosticsInternal(readResponse.DiagnosticsContext);

            if (readResponse.StatusCode != HttpStatusCode.Conflict)
            {
                return(await this.ClientContext.ResponseFactory.CreateContainerResponseAsync(container, Task.FromResult(createResponse)));
            }

            // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create
            // so for the remaining ones we should do a Read instead of throwing Conflict exception
            ResponseMessage readResponseAfterCreate = await container.ReadContainerStreamAsync(
                cancellationToken : cancellationToken);

            // Merge the previous message diagnostics
            createResponse.DiagnosticsContext.AddDiagnosticsInternal(readResponse.DiagnosticsContext);
            return(await this.ClientContext.ResponseFactory.CreateContainerResponseAsync(container, Task.FromResult(readResponseAfterCreate)));
        }
 internal static string GetContinuationToken(ResponseMessage httpResponseMessage)
 {
     return(httpResponseMessage.Headers.Continuation);
 }
Beispiel #7
0
 public override StoredProcedureExecuteResponse <T> CreateStoredProcedureExecuteResponse <T>(ResponseMessage responseMessage)
 {
     return(this.ProcessMessage(responseMessage, (cosmosResponseMessage) =>
     {
         T item = this.ToObjectpublic <T>(cosmosResponseMessage);
         return new StoredProcedureExecuteResponse <T>(
             cosmosResponseMessage.StatusCode,
             cosmosResponseMessage.Headers,
             item,
             cosmosResponseMessage.Diagnostics);
     }));
 }
        public async Task <ContainerResponse> CreateContainerIfNotExistsAsync(
            CosmosDiagnosticsContext diagnosticsContext,
            ContainerProperties containerProperties,
            ThroughputProperties throughputProperties,
            RequestOptions requestOptions,
            CancellationToken cancellationToken)
        {
            if (containerProperties == null)
            {
                throw new ArgumentNullException(nameof(containerProperties));
            }

            this.ValidateContainerProperties(containerProperties);

            ContainerCore container = (ContainerCore)this.GetContainer(containerProperties.Id);

            using (ResponseMessage readResponse = await container.ReadContainerStreamAsync(
                       diagnosticsContext: diagnosticsContext,
                       cancellationToken: cancellationToken))
            {
                if (readResponse.StatusCode != HttpStatusCode.NotFound)
                {
                    ContainerResponse retrivedContainerResponse = this.ClientContext.ResponseFactory.CreateContainerResponse(
                        container,
                        readResponse);
                    if (!retrivedContainerResponse.Resource.PartitionKeyPath.Equals(containerProperties.PartitionKeyPath))
                    {
                        throw new ArgumentException(
                                  string.Format(
                                      ClientResources.PartitionKeyPathConflict,
                                      containerProperties.PartitionKeyPath,
                                      containerProperties.Id,
                                      retrivedContainerResponse.Resource.PartitionKeyPath),
                                  nameof(containerProperties.PartitionKey));
                    }

                    return(retrivedContainerResponse);
                }
            }

            this.ValidateContainerProperties(containerProperties);
            using (ResponseMessage createResponse = await this.CreateContainerStreamAsync(
                       diagnosticsContext,
                       containerProperties,
                       throughputProperties,
                       requestOptions,
                       cancellationToken))
            {
                if (createResponse.StatusCode != HttpStatusCode.Conflict)
                {
                    return(this.ClientContext.ResponseFactory.CreateContainerResponse(container, createResponse));
                }
            }

            // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create
            // so for the remaining ones we should do a Read instead of throwing Conflict exception
            using (ResponseMessage readResponseAfterCreate = await container.ReadContainerStreamAsync(
                       diagnosticsContext: diagnosticsContext,
                       cancellationToken: cancellationToken))
            {
                return(this.ClientContext.ResponseFactory.CreateContainerResponse(container, readResponseAfterCreate));
            }
        }
 public abstract Task <Documents.ShouldRetryResult> HandleSplitAsync(
     ContainerCore containerCore,
     ResponseMessage responseMessage,
     CancellationToken cancellationToken);
        private async Task<ResponseMessage> ReadNextInternalAsync(
            CosmosDiagnosticsContext diagnostics,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            Stream stream = null;
            OperationType operation = OperationType.ReadFeed;
            if (this.querySpec != null)
            {
                stream = this.clientContext.SerializerCore.ToStreamSqlQuerySpec(this.querySpec, this.resourceType);
                operation = OperationType.Query;
            }

            if (this.feedTokenInternal == null)
            {
                TryCatch<FeedTokenInternal> tryCatchFeedTokeninternal = await this.TryInitializeFeedTokenAsync(cancellationToken);
                if (!tryCatchFeedTokeninternal.Succeeded)
                {
                    if (tryCatchFeedTokeninternal.Exception.InnerException is CosmosException cosmosException)
                    {
                        return cosmosException.ToCosmosResponseMessage(new RequestMessage(method: null, requestUri: null, diagnosticsContext: diagnostics));
                    }

                    return CosmosExceptionFactory.CreateInternalServerErrorException(
                        message: tryCatchFeedTokeninternal.Exception.InnerException.Message,
                        innerException: tryCatchFeedTokeninternal.Exception.InnerException,
                        diagnosticsContext: diagnostics).ToCosmosResponseMessage(new RequestMessage(method: null, requestUri: null, diagnosticsContext: diagnostics));
                }

                this.feedTokenInternal = tryCatchFeedTokeninternal.Result;
            }

            ResponseMessage response = await this.clientContext.ProcessResourceOperationStreamAsync(
               resourceUri: this.resourceLink,
               resourceType: this.resourceType,
               operationType: operation,
               requestOptions: this.requestOptions,
               cosmosContainerCore: null,
               partitionKey: this.requestOptions?.PartitionKey,
               streamPayload: stream,
               requestEnricher: request =>
               {
                   QueryRequestOptions.FillContinuationToken(request, this.ContinuationToken);
                   if (this.querySpec != null)
                   {
                       request.Headers.Add(HttpConstants.HttpHeaders.ContentType, MediaTypes.QueryJson);
                       request.Headers.Add(HttpConstants.HttpHeaders.IsQuery, bool.TrueString);
                   }

                   this.feedTokenInternal?.EnrichRequest(request);
               },
               diagnosticsContext: diagnostics,
               cancellationToken: cancellationToken);

            // Retry in case of splits or other scenarios only on partitioned resources
            if (this.containerCore != null
                && await this.feedTokenInternal.ShouldRetryAsync(this.containerCore, response, cancellationToken))
            {
                return await this.ReadNextInternalAsync(diagnostics, cancellationToken);
            }

            if (response.IsSuccessStatusCode)
            {
                this.feedTokenInternal.UpdateContinuation(response.Headers.ContinuationToken);
                this.ContinuationToken = this.feedTokenInternal.GetContinuation();
                this.hasMoreResultsInternal = !this.feedTokenInternal.IsDone;
            }
            else
            {
                this.hasMoreResultsInternal = false;
            }

            return response;
        }
 public abstract Documents.ShouldRetryResult HandleChangeFeedNotModified(ResponseMessage responseMessage);
Beispiel #12
0
 public override FeedResponse <T> CreateQueryFeedUserTypeResponse <T>(
     ResponseMessage responseMessage)
 {
     return(this.CreateQueryFeedResponseHelper <T>(
                responseMessage));
 }
Beispiel #13
0
 public T ToObjectpublic <T>(ResponseMessage responseMessage)
 {
     if (responseMessage.Content == null)
     {
         return(default);
Beispiel #14
0
        internal static async Task <TransactionalBatchResponse> FromResponseMessageAsync(
            ResponseMessage responseMessage,
            ServerBatchRequest serverRequest,
            CosmosSerializerCore serializer,
            bool shouldPromoteOperationStatus = true)
        {
            using (responseMessage)
            {
                TransactionalBatchResponse response = null;
                if (responseMessage.Content != null)
                {
                    Stream content = responseMessage.Content;

                    // Shouldn't be the case practically, but handle it for safety.
                    if (!responseMessage.Content.CanSeek)
                    {
                        content = new MemoryStream();
                        await responseMessage.Content.CopyToAsync(content);
                    }

                    if (content.ReadByte() == (int)HybridRowVersion.V1)
                    {
                        content.Position = 0;
                        response         = await TransactionalBatchResponse.PopulateFromContentAsync(
                            content,
                            responseMessage,
                            serverRequest,
                            serializer,
                            shouldPromoteOperationStatus);

                        if (response == null)
                        {
                            // Convert any payload read failures as InternalServerError
                            response = new TransactionalBatchResponse(
                                HttpStatusCode.InternalServerError,
                                SubStatusCodes.Unknown,
                                ClientResources.ServerResponseDeserializationFailure,
                                responseMessage.Headers.RequestCharge,
                                responseMessage.Headers.RetryAfter,
                                responseMessage.Headers.ActivityId,
                                responseMessage.DiagnosticsContext,
                                serverRequest.Operations,
                                serializer);
                        }
                    }
                }

                if (response == null)
                {
                    response = new TransactionalBatchResponse(
                        responseMessage.StatusCode,
                        responseMessage.Headers.SubStatusCode,
                        responseMessage.ErrorMessage,
                        responseMessage.Headers.RequestCharge,
                        responseMessage.Headers.RetryAfter,
                        responseMessage.Headers.ActivityId,
                        responseMessage.DiagnosticsContext,
                        serverRequest.Operations,
                        serializer);
                }

                if (response.results == null || response.results.Count != serverRequest.Operations.Count)
                {
                    if (responseMessage.IsSuccessStatusCode)
                    {
                        // Server should be guaranteeing number of results equal to operations when
                        // batch request is successful - so fail as InternalServerError if this is not the case.
                        response = new TransactionalBatchResponse(
                            HttpStatusCode.InternalServerError,
                            SubStatusCodes.Unknown,
                            ClientResources.InvalidServerResponse,
                            responseMessage.Headers.RequestCharge,
                            responseMessage.Headers.RetryAfter,
                            responseMessage.Headers.ActivityId,
                            responseMessage.DiagnosticsContext,
                            serverRequest.Operations,
                            serializer);
                    }

                    // When the overall response status code is TooManyRequests, propagate the RetryAfter into the individual operations.
                    int retryAfterMilliseconds = 0;

                    if ((int)responseMessage.StatusCode == (int)StatusCodes.TooManyRequests)
                    {
                        if (!responseMessage.Headers.TryGetValue(HttpConstants.HttpHeaders.RetryAfterInMilliseconds, out string retryAfter) ||
                            retryAfter == null ||
                            !int.TryParse(retryAfter, out retryAfterMilliseconds))
                        {
                            retryAfterMilliseconds = 0;
                        }
                    }

                    response.CreateAndPopulateResults(serverRequest.Operations, retryAfterMilliseconds);
                }

                return(response);
            }
        }
Beispiel #15
0
 public override FeedResponse <T> CreateItemFeedResponse <T>(ResponseMessage responseMessage)
 {
     return(this.CreateQueryFeedResponseHelper <T>(
                responseMessage,
                Documents.ResourceType.Document));
 }
Beispiel #16
0
        private static async Task <TransactionalBatchResponse> PopulateFromContentAsync(
            Stream content,
            ResponseMessage responseMessage,
            ServerBatchRequest serverRequest,
            CosmosSerializerCore serializer,
            bool shouldPromoteOperationStatus)
        {
            List <TransactionalBatchOperationResult> results = new List <TransactionalBatchOperationResult>();

            // content is ensured to be seekable in caller.
            int resizerInitialCapacity = (int)content.Length;

            Result res = await content.ReadRecordIOAsync(
                record =>
            {
                Result r = TransactionalBatchOperationResult.ReadOperationResult(record, out TransactionalBatchOperationResult operationResult);
                if (r != Result.Success)
                {
                    return(r);
                }

                results.Add(operationResult);
                return(r);
            },
                resizer : new MemorySpanResizer <byte>(resizerInitialCapacity));

            if (res != Result.Success)
            {
                return(null);
            }

            HttpStatusCode responseStatusCode    = responseMessage.StatusCode;
            SubStatusCodes responseSubStatusCode = responseMessage.Headers.SubStatusCode;

            // Promote the operation error status as the Batch response error status if we have a MultiStatus response
            // to provide users with status codes they are used to.
            if ((int)responseMessage.StatusCode == (int)StatusCodes.MultiStatus &&
                shouldPromoteOperationStatus)
            {
                foreach (TransactionalBatchOperationResult result in results)
                {
                    if ((int)result.StatusCode != (int)StatusCodes.FailedDependency)
                    {
                        responseStatusCode    = result.StatusCode;
                        responseSubStatusCode = result.SubStatusCode;
                        break;
                    }
                }
            }

            TransactionalBatchResponse response = new TransactionalBatchResponse(
                responseStatusCode,
                responseSubStatusCode,
                responseMessage.ErrorMessage,
                responseMessage.Headers.RequestCharge,
                responseMessage.Headers.RetryAfter,
                responseMessage.Headers.ActivityId,
                responseMessage.DiagnosticsContext,
                serverRequest.Operations,
                serializer);

            response.results = results;
            return(response);
        }
        private async Task <ResponseMessage> ReadNextInternalAsync(
            CosmosDiagnosticsContext diagnostics,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            ResponseMessage responseMessage = await this.clientContext.ProcessResourceOperationStreamAsync(
                resourceUri : this.containerCore.LinkUri,
                resourceType : ResourceType.Document,
                operationType : OperationType.ReadFeed,
                requestOptions : this.queryRequestOptions,
                cosmosContainerCore : this.containerCore,
                partitionKey : this.queryRequestOptions?.PartitionKey,
                streamPayload : null,
                requestEnricher : request =>
            {
                FeedRangeVisitor feedRangeVisitor = new FeedRangeVisitor(request);
                this.FeedRangeInternal.Accept(feedRangeVisitor);
                this.FeedRangeContinuation.Accept(feedRangeVisitor, QueryRequestOptions.FillContinuationToken);
            },
                diagnosticsContext : diagnostics,
                cancellationToken : cancellationToken);

            ShouldRetryResult shouldRetryOnSplit = await this.FeedRangeContinuation.HandleSplitAsync(this.containerCore, responseMessage, cancellationToken);

            if (shouldRetryOnSplit.ShouldRetry)
            {
                return(await this.ReadNextInternalAsync(diagnostics, cancellationToken));
            }

            if (responseMessage.Content != null)
            {
                // Rewrite the payload to be in the specified format.
                // If it's already in the correct format, then the following will be a memcpy.
                MemoryStream memoryStream;
                if (responseMessage.Content is MemoryStream responseContentAsMemoryStream)
                {
                    memoryStream = responseContentAsMemoryStream;
                }
                else
                {
                    memoryStream = new MemoryStream();
                    await responseMessage.Content.CopyToAsync(memoryStream);
                }

                ReadOnlyMemory <byte> buffer;
                if (memoryStream.TryGetBuffer(out ArraySegment <byte> segment))
                {
                    buffer = segment.Array.AsMemory().Slice(start: segment.Offset, length: segment.Count);
                }
                else
                {
                    buffer = memoryStream.ToArray();
                }

                IJsonReader jsonReader = JsonReader.Create(buffer);
                IJsonWriter jsonWriter;
                if (this.queryRequestOptions?.CosmosSerializationFormatOptions != null)
                {
                    jsonWriter = this.queryRequestOptions.CosmosSerializationFormatOptions.CreateCustomWriterCallback();
                }
                else
                {
                    jsonWriter = NewtonsoftToCosmosDBWriter.CreateTextWriter();
                }

                jsonWriter.WriteAll(jsonReader);

                ReadOnlyMemory <byte> result = jsonWriter.GetResult();
                MemoryStream          rewrittenMemoryStream;
                if (MemoryMarshal.TryGetArray(result, out ArraySegment <byte> rewrittenSegment))
                {
                    rewrittenMemoryStream = new MemoryStream(rewrittenSegment.Array, index: rewrittenSegment.Offset, count: rewrittenSegment.Count);
                }
                else
                {
                    rewrittenMemoryStream = new MemoryStream(result.ToArray());
                }

                responseMessage.Content = rewrittenMemoryStream;
            }

            if (responseMessage.IsSuccessStatusCode)
            {
                this.FeedRangeContinuation.ReplaceContinuation(responseMessage.Headers.ContinuationToken);
                this.hasMoreResultsInternal = !this.FeedRangeContinuation.IsDone;
                return(FeedRangeResponse.CreateSuccess(responseMessage, this.FeedRangeContinuation));
            }
            else
            {
                this.hasMoreResultsInternal = false;
                return(FeedRangeResponse.CreateFailure(responseMessage));
            }
        }
        // Extracted partition key might be invalid as CollectionCache might be stale.
        // Stale collection cache is refreshed through PartitionKeyMismatchRetryPolicy
        // and partition-key is extracted again.
        internal async Task <ResponseMessage> ExtractPartitionKeyAndProcessItemStreamAsync <T>(
            PartitionKey?partitionKey,
            string itemId,
            T item,
            OperationType operationType,
            RequestOptions requestOptions,
            CancellationToken cancellationToken)
        {
            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            using (diagnosticsContext.CreateOverallScope("ItemStream"))
            {
                Stream itemStream;
                using (diagnosticsContext.CreateScope("ItemSerialize"))
                {
                    itemStream = this.ClientContext.SerializerCore.ToStream <T>(item);
                }

                // User specified PK value, no need to extract it
                if (partitionKey.HasValue)
                {
                    return(await this.ProcessItemStreamAsync(
                               partitionKey,
                               itemId,
                               itemStream,
                               operationType,
                               requestOptions,
                               diagnosticsScope : diagnosticsContext,
                               cancellationToken : cancellationToken));
                }

                PartitionKeyMismatchRetryPolicy requestRetryPolicy = null;
                while (true)
                {
                    using (diagnosticsContext.CreateScope("ExtractPkValue"))
                    {
                        partitionKey = await this.GetPartitionKeyValueFromStreamAsync(itemStream, cancellationToken);
                    }

                    ResponseMessage responseMessage = await this.ProcessItemStreamAsync(
                        partitionKey,
                        itemId,
                        itemStream,
                        operationType,
                        requestOptions,
                        diagnosticsScope : diagnosticsContext,
                        cancellationToken : cancellationToken);

                    if (responseMessage.IsSuccessStatusCode)
                    {
                        return(responseMessage);
                    }

                    if (requestRetryPolicy == null)
                    {
                        requestRetryPolicy = new PartitionKeyMismatchRetryPolicy(await this.ClientContext.DocumentClient.GetCollectionCacheAsync(), null);
                    }

                    ShouldRetryResult retryResult = await requestRetryPolicy.ShouldRetryAsync(responseMessage, cancellationToken);

                    if (!retryResult.ShouldRetry)
                    {
                        return(responseMessage);
                    }
                }
            }
        }
Beispiel #19
0
        public void EnsureSuccessStatusCode_DontThrowOnSuccess()
        {
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.EnsureSuccessStatusCode();
        }
        public async Task <ContainerResponse> CreateContainerIfNotExistsAsync(
            CosmosDiagnosticsContext diagnosticsContext,
            ContainerProperties containerProperties,
            ThroughputProperties throughputProperties,
            RequestOptions requestOptions,
            ITrace trace,
            CancellationToken cancellationToken)
        {
            if (containerProperties == null)
            {
                throw new ArgumentNullException(nameof(containerProperties));
            }

            this.ValidateContainerProperties(containerProperties);

            double        totalRequestCharge = 0;
            ContainerCore container          = (ContainerCore)this.GetContainer(containerProperties.Id);

            using (ResponseMessage readResponse = await container.ReadContainerStreamAsync(
                       diagnosticsContext: diagnosticsContext,
                       requestOptions: requestOptions,
                       trace: trace,
                       cancellationToken: cancellationToken))
            {
                totalRequestCharge = readResponse.Headers.RequestCharge;

                if (readResponse.StatusCode != HttpStatusCode.NotFound)
                {
                    ContainerResponse retrivedContainerResponse = this.ClientContext.ResponseFactory.CreateContainerResponse(
                        container,
                        readResponse);

                    if (containerProperties.PartitionKey.Kind != Documents.PartitionKind.MultiHash)
                    {
                        if (!retrivedContainerResponse.Resource.PartitionKeyPath.Equals(containerProperties.PartitionKeyPath))
                        {
                            throw new ArgumentException(
                                      string.Format(
                                          ClientResources.PartitionKeyPathConflict,
                                          containerProperties.PartitionKeyPath,
                                          containerProperties.Id,
                                          retrivedContainerResponse.Resource.PartitionKeyPath),
                                      nameof(containerProperties.PartitionKey));
                        }
                    }
#if INTERNAL || SUBPARTITIONING
                    else
                    {
                        IReadOnlyList <string> retrivedPartitionKeyPaths = retrivedContainerResponse.Resource.PartitionKeyPaths;
                        IReadOnlyList <string> receivedPartitionKeyPaths = containerProperties.PartitionKeyPaths;

                        if (retrivedPartitionKeyPaths.Count != receivedPartitionKeyPaths.Count || !Enumerable.SequenceEqual(retrivedPartitionKeyPaths, receivedPartitionKeyPaths))
                        {
                            throw new ArgumentException(
                                      string.Format(
                                          ClientResources.PartitionKeyPathConflict,
                                          string.Join(",", containerProperties.PartitionKeyPaths),
                                          containerProperties.Id,
                                          string.Join(",", retrivedContainerResponse.Resource.PartitionKeyPaths)),
                                      nameof(containerProperties.PartitionKey));
                        }
                    }
#endif
                    return(retrivedContainerResponse);
                }
            }

            this.ValidateContainerProperties(containerProperties);
            using (ResponseMessage createResponse = await this.CreateContainerStreamAsync(
                       diagnosticsContext,
                       containerProperties,
                       throughputProperties,
                       requestOptions,
                       trace,
                       cancellationToken))
            {
                totalRequestCharge += createResponse.Headers.RequestCharge;
                createResponse.Headers.RequestCharge = totalRequestCharge;

                if (createResponse.StatusCode != HttpStatusCode.Conflict)
                {
                    return(this.ClientContext.ResponseFactory.CreateContainerResponse(container, createResponse));
                }
            }

            // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create
            // so for the remaining ones we should do a Read instead of throwing Conflict exception
            using (ResponseMessage readResponseAfterCreate = await container.ReadContainerStreamAsync(
                       diagnosticsContext: diagnosticsContext,
                       requestOptions: requestOptions,
                       trace: trace,
                       cancellationToken: cancellationToken))
            {
                totalRequestCharge += readResponseAfterCreate.Headers.RequestCharge;
                readResponseAfterCreate.Headers.RequestCharge = totalRequestCharge;

                return(this.ClientContext.ResponseFactory.CreateContainerResponse(container, readResponseAfterCreate));
            }
        }
Beispiel #21
0
        public void EnsureSuccessStatusCode_ThrowsOnFailure()
        {
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.NotFound);

            Assert.ThrowsException <CosmosException>(() => responseMessage.EnsureSuccessStatusCode());
        }
Beispiel #22
0
        // Extracted partition key might be invalid as CollectionCache might be stale.
        // Stale collection cache is refreshed through PartitionKeyMismatchRetryPolicy
        // and partition-key is extracted again.
        private async Task <ResponseMessage> ExtractPartitionKeyAndProcessItemStreamAsync <T>(
            PartitionKey?partitionKey,
            string itemId,
            T item,
            OperationType operationType,
            ItemRequestOptions requestOptions,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            if (diagnosticsContext == null)
            {
                throw new ArgumentNullException(nameof(diagnosticsContext));
            }

            Stream itemStream;

            using (diagnosticsContext.CreateScope("ItemSerialize"))
            {
                itemStream = this.ClientContext.SerializerCore.ToStream <T>(item);
            }

            // User specified PK value, no need to extract it
            if (partitionKey.HasValue)
            {
                PartitionKeyDefinition pKeyDefinition = await this.GetPartitionKeyDefinitionAsync();

                if (partitionKey.HasValue && partitionKey.Value != PartitionKey.None && partitionKey.Value.InternalKey.Components.Count != pKeyDefinition.Paths.Count)
                {
                    throw new ArgumentException(RMResources.MissingPartitionKeyValue);
                }

                return(await this.ProcessItemStreamAsync(
                           partitionKey,
                           itemId,
                           itemStream,
                           operationType,
                           requestOptions,
                           diagnosticsContext : diagnosticsContext,
                           cancellationToken : cancellationToken));
            }

            PartitionKeyMismatchRetryPolicy requestRetryPolicy = null;

            while (true)
            {
                using (diagnosticsContext.CreateScope("ExtractPkValue"))
                {
                    partitionKey = await this.GetPartitionKeyValueFromStreamAsync(itemStream, cancellationToken);
                }

                ResponseMessage responseMessage = await this.ProcessItemStreamAsync(
                    partitionKey,
                    itemId,
                    itemStream,
                    operationType,
                    requestOptions,
                    diagnosticsContext : diagnosticsContext,
                    cancellationToken : cancellationToken);

                if (responseMessage.IsSuccessStatusCode)
                {
                    return(responseMessage);
                }

                if (requestRetryPolicy == null)
                {
                    requestRetryPolicy = new PartitionKeyMismatchRetryPolicy(await this.ClientContext.DocumentClient.GetCollectionCacheAsync(), null);
                }

                ShouldRetryResult retryResult = await requestRetryPolicy.ShouldRetryAsync(responseMessage, cancellationToken);

                if (!retryResult.ShouldRetry)
                {
                    return(responseMessage);
                }
            }
        }
        private static TryCatch <QueryPage> GetCosmosElementResponse(
            Guid clientQueryCorrelationId,
            QueryRequestOptions requestOptions,
            ResourceType resourceType,
            ResponseMessage cosmosResponseMessage,
            PartitionKeyRangeIdentity partitionKeyRangeIdentity,
            Action <QueryPageDiagnostics> queryPageDiagnostics)
        {
            using (cosmosResponseMessage)
            {
                QueryPageDiagnostics queryPage = new QueryPageDiagnostics(
                    clientQueryCorrelationId: clientQueryCorrelationId,
                    partitionKeyRangeId: partitionKeyRangeIdentity.PartitionKeyRangeId,
                    queryMetricText: cosmosResponseMessage.Headers.QueryMetricsText,
                    indexUtilizationText: cosmosResponseMessage.Headers[HttpConstants.HttpHeaders.IndexUtilization],
                    diagnosticsContext: cosmosResponseMessage.DiagnosticsContext);
                queryPageDiagnostics(queryPage);

                if (!cosmosResponseMessage.IsSuccessStatusCode)
                {
                    CosmosException exception;
                    if (cosmosResponseMessage.CosmosException != null)
                    {
                        exception = cosmosResponseMessage.CosmosException;
                    }
                    else
                    {
                        exception = new CosmosException(
                            cosmosResponseMessage.ErrorMessage,
                            cosmosResponseMessage.StatusCode,
                            (int)cosmosResponseMessage.Headers.SubStatusCode,
                            cosmosResponseMessage.Headers.ActivityId,
                            cosmosResponseMessage.Headers.RequestCharge);
                    }

                    return(TryCatch <QueryPage> .FromException(exception));
                }

                if (!(cosmosResponseMessage.Content is MemoryStream memoryStream))
                {
                    memoryStream = new MemoryStream();
                    cosmosResponseMessage.Content.CopyTo(memoryStream);
                }

                long        responseLengthBytes = memoryStream.Length;
                CosmosArray documents           = CosmosQueryClientCore.ParseElementsFromRestStream(
                    memoryStream,
                    resourceType,
                    requestOptions.CosmosSerializationFormatOptions);

                CosmosQueryExecutionInfo cosmosQueryExecutionInfo;
                if (cosmosResponseMessage.Headers.TryGetValue(QueryExecutionInfoHeader, out string queryExecutionInfoString))
                {
                    cosmosQueryExecutionInfo = JsonConvert.DeserializeObject <CosmosQueryExecutionInfo>(queryExecutionInfoString);
                }
                else
                {
                    cosmosQueryExecutionInfo = default;
                }

                QueryState queryState;
                if (cosmosResponseMessage.Headers.ContinuationToken != null)
                {
                    queryState = new QueryState(CosmosString.Create(cosmosResponseMessage.Headers.ContinuationToken));
                }
                else
                {
                    queryState = default;
                }

                QueryPage response = new QueryPage(
                    documents,
                    cosmosResponseMessage.Headers.RequestCharge,
                    cosmosResponseMessage.Headers.ActivityId,
                    responseLengthBytes,
                    cosmosQueryExecutionInfo,
                    disallowContinuationTokenMessage: null,
                    queryState);

                return(TryCatch <QueryPage> .FromResult(response));
            }
        }
Beispiel #24
0
        internal static async Task <BatchResponse> FromResponseMessageAsync(
            ResponseMessage responseMessage,
            ServerBatchRequest serverRequest,
            CosmosSerializer serializer)
        {
            using (responseMessage)
            {
                BatchResponse response = null;
                if (responseMessage.IsSuccessStatusCode && responseMessage.Content != null)
                {
                    response = await BatchResponse.PopulateFromContentAsync(responseMessage, serverRequest, serializer);

                    if (response == null)
                    {
                        // Convert any payload read failures as InternalServerError
                        response = new BatchResponse(
                            HttpStatusCode.InternalServerError,
                            SubStatusCodes.Unknown,
                            ClientResources.ServerResponseDeserializationFailure,
                            responseMessage.Headers.RequestCharge,
                            responseMessage.Headers.RetryAfter,
                            responseMessage.Headers.ActivityId,
                            serverRequest,
                            serializer);
                    }
                }
                else
                {
                    response = new BatchResponse(
                        responseMessage.StatusCode,
                        responseMessage.Headers.SubStatusCode,
                        responseMessage.ErrorMessage,
                        responseMessage.Headers.RequestCharge,
                        responseMessage.Headers.RetryAfter,
                        responseMessage.Headers.ActivityId,
                        serverRequest,
                        serializer);
                }

                if (response.results == null || response.results.Count != serverRequest.Operations.Count)
                {
                    if (responseMessage.IsSuccessStatusCode)
                    {
                        // Server should be guaranteeing number of results equal to operations when
                        // batch request is successful - so fail as InternalServerError if this is not the case.
                        response = new BatchResponse(
                            HttpStatusCode.InternalServerError,
                            SubStatusCodes.Unknown,
                            ClientResources.InvalidServerResponse,
                            responseMessage.Headers.RequestCharge,
                            responseMessage.Headers.RetryAfter,
                            responseMessage.Headers.ActivityId,
                            serverRequest,
                            serializer);
                    }

                    // When the overall response status code is TooManyRequests, propagate the RetryAfter into the individual operations.
                    int retryAfterMilliseconds = 0;

                    if ((int)responseMessage.StatusCode == (int)StatusCodes.TooManyRequests)
                    {
                        if (!responseMessage.Headers.TryGetValue(HttpConstants.HttpHeaders.RetryAfterInMilliseconds, out string retryAfter) ||
                            retryAfter == null ||
                            !int.TryParse(retryAfter, out retryAfterMilliseconds))
                        {
                            retryAfterMilliseconds = 0;
                        }
                    }

                    response.results = new List <BatchOperationResult>();
                    for (int i = 0; i < serverRequest.Operations.Count; i++)
                    {
                        response.results.Add(
                            new BatchOperationResult(response.StatusCode)
                        {
                            SubStatusCode = response.SubStatusCode,
                            RetryAfter    = TimeSpan.FromMilliseconds(retryAfterMilliseconds),
                        });
                    }
                }

                return(response);
            }
        }
        private async Task <ResponseMessage> ReadNextInternalAsync(
            CosmosDiagnosticsContext diagnostics,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Stream        stream    = null;
            OperationType operation = OperationType.ReadFeed;

            if (this.querySpec != null)
            {
                stream    = this.clientContext.SerializerCore.ToStreamSqlQuerySpec(this.querySpec, this.resourceType);
                operation = OperationType.Query;
            }

            ResponseMessage responseMessage = await this.clientContext.ProcessResourceOperationStreamAsync(
                resourceUri : this.containerCore.LinkUri,
                resourceType : this.resourceType,
                operationType : operation,
                requestOptions : this.queryRequestOptions,
                cosmosContainerCore : this.containerCore,
                partitionKey : this.queryRequestOptions?.PartitionKey,
                streamPayload : stream,
                requestEnricher : request =>
            {
                // FeedRangeContinuationRequestMessagePopulatorVisitor needs to run before FeedRangeRequestMessagePopulatorVisitor,
                // since they both set EPK range headers and in the case of split we need to run on the child range and not the parent range.
                FeedRangeContinuationRequestMessagePopulatorVisitor feedRangeContinuationVisitor = new FeedRangeContinuationRequestMessagePopulatorVisitor(
                    request,
                    QueryRequestOptions.FillContinuationToken);
                this.FeedRangeContinuation.Accept(feedRangeContinuationVisitor);

                FeedRangeRequestMessagePopulatorVisitor feedRangeVisitor = new FeedRangeRequestMessagePopulatorVisitor(request);
                this.FeedRangeInternal.Accept(feedRangeVisitor);

                if (this.querySpec != null)
                {
                    request.Headers.Add(HttpConstants.HttpHeaders.ContentType, MediaTypes.QueryJson);
                    request.Headers.Add(HttpConstants.HttpHeaders.IsQuery, bool.TrueString);
                }
            },
                diagnosticsContext : diagnostics,
                cancellationToken : cancellationToken);

            ShouldRetryResult shouldRetryOnSplit = await this.FeedRangeContinuation.HandleSplitAsync(this.containerCore, responseMessage, cancellationToken);

            if (shouldRetryOnSplit.ShouldRetry)
            {
                return(await this.ReadNextInternalAsync(diagnostics, cancellationToken));
            }

            if (responseMessage.Content != null)
            {
                await CosmosElementSerializer.RewriteStreamAsTextAsync(responseMessage, this.queryRequestOptions);
            }

            if (responseMessage.IsSuccessStatusCode)
            {
                this.FeedRangeContinuation.ReplaceContinuation(responseMessage.Headers.ContinuationToken);
                this.hasMoreResultsInternal = !this.FeedRangeContinuation.IsDone;
                return(FeedRangeResponse.CreateSuccess(responseMessage, this.FeedRangeContinuation));
            }
            else
            {
                this.hasMoreResultsInternal = false;
                return(FeedRangeResponse.CreateFailure(responseMessage));
            }
        }