private async Task <PartitionKeyRangeBatchExecutionResult> ExecuteAsync(
            PartitionKeyRangeServerBatchRequest serverRequest,
            CancellationToken cancellationToken)
        {
            CosmosDiagnosticsContext diagnosticsContext = new CosmosDiagnosticsContextCore();
            CosmosDiagnosticScope    limiterScope       = diagnosticsContext.CreateScope("BatchAsyncContainerExecutor.Limiter");
            SemaphoreSlim            limiter            = this.GetOrAddLimiterForPartitionKeyRange(serverRequest.PartitionKeyRangeId);

            using (await limiter.UsingWaitAsync(cancellationToken))
            {
                limiterScope.Dispose();
                using (Stream serverRequestPayload = serverRequest.TransferBodyStream())
                {
                    Debug.Assert(serverRequestPayload != null, "Server request payload expected to be non-null");
                    ResponseMessage responseMessage = await this.cosmosClientContext.ProcessResourceOperationStreamAsync(
                        this.cosmosContainer.LinkUri,
                        ResourceType.Document,
                        OperationType.Batch,
                        new RequestOptions(),
                        cosmosContainerCore : this.cosmosContainer,
                        partitionKey : null,
                        streamPayload : serverRequestPayload,
                        requestEnricher : requestMessage => BatchAsyncContainerExecutor.AddHeadersToRequestMessage(requestMessage, serverRequest.PartitionKeyRangeId),
                        diagnosticsContext : diagnosticsContext,
                        cancellationToken : cancellationToken).ConfigureAwait(false);

                    using (diagnosticsContext.CreateScope("BatchAsyncContainerExecutor.ToResponse"))
                    {
                        TransactionalBatchResponse serverResponse = await TransactionalBatchResponse.FromResponseMessageAsync(
                            responseMessage,
                            serverRequest,
                            this.cosmosClientContext.SerializerCore,
                            shouldPromoteOperationStatus : true,
                            shouldPerformDecryption : false,
                            cancellationToken).ConfigureAwait(false);

                        return(new PartitionKeyRangeBatchExecutionResult(serverRequest.PartitionKeyRangeId, serverRequest.Operations, serverResponse));
                    }
                }
            }
        }
Esempio n. 2
0
        private async Task <PartitionKeyRangeBatchExecutionResult> ExecuteAsync(
            PartitionKeyRangeServerBatchRequest serverRequest,
            ITrace trace,
            CancellationToken cancellationToken)
        {
            SemaphoreSlim limiter = this.GetOrAddLimiterForPartitionKeyRange(serverRequest.PartitionKeyRangeId);

            using (await limiter.UsingWaitAsync(trace, cancellationToken))
            {
                using (Stream serverRequestPayload = serverRequest.TransferBodyStream())
                {
                    Debug.Assert(serverRequestPayload != null, "Server request payload expected to be non-null");
                    ResponseMessage responseMessage = await this.cosmosClientContext.ProcessResourceOperationStreamAsync(
                        this.cosmosContainer.LinkUri,
                        ResourceType.Document,
                        OperationType.Batch,
                        new RequestOptions(),
                        cosmosContainerCore : this.cosmosContainer,
                        feedRange : null,
                        streamPayload : serverRequestPayload,
                        requestEnricher : requestMessage => BatchAsyncContainerExecutor.AddHeadersToRequestMessage(requestMessage, serverRequest),
                        trace : trace,
                        cancellationToken : cancellationToken).ConfigureAwait(false);

                    TransactionalBatchResponse serverResponse = await TransactionalBatchResponse.FromResponseMessageAsync(
                        responseMessage,
                        serverRequest,
                        this.cosmosClientContext.SerializerCore,
                        shouldPromoteOperationStatus : true,
                        trace,
                        cancellationToken).ConfigureAwait(false);

                    return(new PartitionKeyRangeBatchExecutionResult(
                               serverRequest.PartitionKeyRangeId,
                               serverRequest.Operations,
                               serverResponse));
                }
            }
        }
        /// <summary>
        /// Makes a single batch request to the server.
        /// </summary>
        /// <param name="serverRequest">A server request with a set of operations on items.</param>
        /// <param name="trace">The trace.</param>
        /// <param name="cancellationToken"><see cref="CancellationToken"/> representing request cancellation.</param>
        /// <returns>Response from the server.</returns>
        private async Task <TransactionalBatchResponse> ExecuteServerRequestAsync(
            SinglePartitionKeyServerBatchRequest serverRequest,
            ITrace trace,
            CancellationToken cancellationToken)
        {
            using (ITrace executeBatchTrace = trace.StartChild("Execute Batch Request", TraceComponent.Batch, Tracing.TraceLevel.Info))
            {
                using (Stream serverRequestPayload = serverRequest.TransferBodyStream())
                {
                    Debug.Assert(serverRequestPayload != null, "Server request payload expected to be non-null");
                    ResponseMessage responseMessage = await this.clientContext.ProcessResourceOperationStreamAsync(
                        this.container.LinkUri,
                        ResourceType.Document,
                        OperationType.Batch,
                        this.batchOptions,
                        this.container,
                        serverRequest.PartitionKey.HasValue?new FeedRangePartitionKey(serverRequest.PartitionKey.Value) : null,
                        serverRequestPayload,
                        requestMessage =>
                    {
                        requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchRequest, bool.TrueString);
                        requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchAtomic, bool.TrueString);
                        requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchOrdered, bool.TrueString);
                    },
                        executeBatchTrace,
                        cancellationToken);

                    return(await TransactionalBatchResponse.FromResponseMessageAsync(
                               responseMessage,
                               serverRequest,
                               this.clientContext.SerializerCore,
                               shouldPromoteOperationStatus : true,
                               executeBatchTrace,
                               cancellationToken));
                }
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="PartitionKeyRangeBatchResponse"/> class.
        /// </summary>
        /// <param name="originalOperationsCount">Original operations that generated the server responses.</param>
        /// <param name="serverResponse">Response from the server.</param>
        /// <param name="serializerCore">Serializer to deserialize response resource body streams.</param>
        internal PartitionKeyRangeBatchResponse(
            int originalOperationsCount,
            TransactionalBatchResponse serverResponse,
            CosmosSerializerCore serializerCore)
        {
            this.StatusCode = serverResponse.StatusCode;

            this.serverResponse          = serverResponse;
            this.resultsByOperationIndex = new TransactionalBatchOperationResult[originalOperationsCount];

            StringBuilder             errorMessageBuilder = new StringBuilder();
            List <ItemBatchOperation> itemBatchOperations = new List <ItemBatchOperation>();

            // We expect number of results == number of operations here
            for (int index = 0; index < serverResponse.Operations.Count; index++)
            {
                int operationIndex = serverResponse.Operations[index].OperationIndex;
                if (this.resultsByOperationIndex[operationIndex] == null ||
                    this.resultsByOperationIndex[operationIndex].StatusCode == (HttpStatusCode)StatusCodes.TooManyRequests)
                {
                    this.resultsByOperationIndex[operationIndex] = serverResponse[index];
                }
            }

            itemBatchOperations.AddRange(serverResponse.Operations);
            this.Headers = serverResponse.Headers;

            if (!string.IsNullOrEmpty(serverResponse.ErrorMessage))
            {
                errorMessageBuilder.AppendFormat("{0}; ", serverResponse.ErrorMessage);
            }

            this.ErrorMessage   = errorMessageBuilder.Length > 2 ? errorMessageBuilder.ToString(0, errorMessageBuilder.Length - 2) : null;
            this.Operations     = itemBatchOperations;
            this.SerializerCore = serializerCore;
        }
Esempio n. 5
0
        /// <summary>
        /// Makes a single batch request to the server.
        /// </summary>
        /// <param name="serverRequest">A server request with a set of operations on items.</param>
        /// <param name="cancellationToken"><see cref="CancellationToken"/> representing request cancellation.</param>
        /// <returns>Response from the server.</returns>
        private async Task <TransactionalBatchResponse> ExecuteServerRequestAsync(
            SinglePartitionKeyServerBatchRequest serverRequest,
            CancellationToken cancellationToken)
        {
            using (Stream serverRequestPayload = serverRequest.TransferBodyStream())
            {
                Debug.Assert(serverRequestPayload != null, "Server request payload expected to be non-null");
                ResponseMessage responseMessage = await this.clientContext.ProcessResourceOperationStreamAsync(
                    this.container.LinkUri,
                    ResourceType.Document,
                    OperationType.Batch,
                    this.batchOptions,
                    this.container,
                    serverRequest.PartitionKey,
                    serverRequestPayload,
                    requestMessage =>
                {
                    requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchRequest, bool.TrueString);
                    requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchAtomic, bool.TrueString);
                    requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchOrdered, bool.TrueString);
                },
                    diagnosticsContext : this.diagnosticsContext,
                    cancellationToken);

                using (this.diagnosticsContext.CreateScope("TransactionalBatchResponse"))
                {
                    return(await TransactionalBatchResponse.FromResponseMessageAsync(
                               responseMessage,
                               serverRequest,
                               this.clientContext.SerializerCore,
                               shouldPromoteOperationStatus : true,
                               shouldPerformDecryption : true,
                               cancellationToken));
                }
            }
        }
Esempio n. 6
0
        private static async Task <TransactionalBatchResponse> PopulateFromContentAsync(
            Stream content,
            ResponseMessage responseMessage,
            ServerBatchRequest serverRequest,
            CosmosSerializerCore serializer,
            ITrace trace,
            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(
                (Func <ReadOnlyMemory <byte>, Result>)(record =>
            {
                Result r = TransactionalBatchOperationResult.ReadOperationResult(record, out TransactionalBatchOperationResult operationResult);
                if (r != Result.Success)
                {
                    return(r);
                }

                operationResult.Trace = trace;

                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 && (int)result.StatusCode >= (int)StatusCodes.StartingErrorCode)
                    {
                        responseStatusCode    = result.StatusCode;
                        responseSubStatusCode = result.SubStatusCode;
                        break;
                    }
                }
            }

            TransactionalBatchResponse response = new TransactionalBatchResponse(
                responseStatusCode,
                responseSubStatusCode,
                responseMessage.ErrorMessage,
                responseMessage.Headers,
                trace,
                serverRequest.Operations,
                serializer)
            {
                results = results
            };

            return(response);
        }
Esempio n. 7
0
        internal static async Task <TransactionalBatchResponse> FromResponseMessageAsync(
            ResponseMessage responseMessage,
            ServerBatchRequest serverRequest,
            CosmosSerializerCore serializer,
            bool shouldPromoteOperationStatus,
            ITrace trace,
            CancellationToken cancellationToken)
        {
            using (ITrace createResponseTrace = trace.StartChild("Create Trace", TraceComponent.Batch, TraceLevel.Info))
            {
                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,
                                trace,
                                shouldPromoteOperationStatus);

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

                    if (response == null)
                    {
                        response = new TransactionalBatchResponse(
                            responseMessage.StatusCode,
                            responseMessage.Headers.SubStatusCode,
                            responseMessage.ErrorMessage,
                            responseMessage.Headers,
                            trace,
                            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,
                                trace,
                                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, trace, retryAfterMilliseconds);
                    }

                    return(response);
                }
            }
        }
Esempio n. 8
0
        internal static async Task <TransactionalBatchResponse> FromResponseMessageAsync(
            ResponseMessage responseMessage,
            ServerBatchRequest serverRequest,
            CosmosSerializerCore serializer,
            bool shouldPromoteOperationStatus,
            bool shouldPerformDecryption,
            CancellationToken cancellationToken)
        {
            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);
                }
                else if (shouldPerformDecryption)
                {
                    for (int index = 0; index < serverRequest.Operations.Count; index++)
                    {
                        ContainerCore containerCore = serverRequest.Operations[index].ContainerCore;
                        TransactionalBatchOperationResult result = response.results[index];
                        result.ResourceStream = await containerCore.ClientContext.DecryptItemAsync(
                            result.ResourceStream,
                            (DatabaseCore)containerCore.Database,
                            responseMessage.DiagnosticsContext,
                            cancellationToken);
                    }
                }

                return(response);
            }
        }