private async Task <PartitionKeyRangeBatchExecutionResult> ExecuteAsync( PartitionKeyRangeServerBatchRequest serverRequest, CancellationToken cancellationToken) { CosmosDiagnosticsContext diagnosticsContext = new CosmosDiagnosticsContext(); 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), diagnosticsScope : diagnosticsContext, cancellationToken : cancellationToken).ConfigureAwait(false); using (diagnosticsContext.CreateScope("BatchAsyncContainerExecutor.ToResponse")) { TransactionalBatchResponse serverResponse = await TransactionalBatchResponse.FromResponseMessageAsync(responseMessage, serverRequest, this.cosmosClientContext.SerializerCore).ConfigureAwait(false); return(new PartitionKeyRangeBatchExecutionResult(serverRequest.PartitionKeyRangeId, serverRequest.Operations, serverResponse)); } } } }
/// <summary> /// Creates an instance of <see cref="PartitionKeyRangeServerBatchRequest"/>. /// In case of direct mode requests, all the operations are expected to belong to the same PartitionKeyRange. /// The body of the request is populated with operations till it reaches the provided maxBodyLength. /// </summary> /// <param name="partitionKeyRangeId">The partition key range id associated with all requests.</param> /// <param name="operations">Operations to be added into this batch request.</param> /// <param name="maxBodyLength">Desired maximum length of the request body.</param> /// <param name="maxOperationCount">Maximum number of operations allowed in the request.</param> /// <param name="ensureContinuousOperationIndexes">Whether to stop adding operations to the request once there is non-continuity in the operation indexes.</param> /// <param name="serializerCore">Serializer to serialize user provided objects to JSON.</param> /// <param name="cancellationToken"><see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A newly created instance of <see cref="PartitionKeyRangeServerBatchRequest"/> and the overflow ItemBatchOperation not being processed.</returns> public static async Task <Tuple <PartitionKeyRangeServerBatchRequest, ArraySegment <ItemBatchOperation> > > CreateAsync( string partitionKeyRangeId, ArraySegment <ItemBatchOperation> operations, int maxBodyLength, int maxOperationCount, bool ensureContinuousOperationIndexes, CosmosSerializerCore serializerCore, CancellationToken cancellationToken) { PartitionKeyRangeServerBatchRequest request = new PartitionKeyRangeServerBatchRequest(partitionKeyRangeId, maxBodyLength, maxOperationCount, serializerCore); ArraySegment <ItemBatchOperation> pendingOperations = await request.CreateBodyStreamAsync(operations, cancellationToken, ensureContinuousOperationIndexes); return(new Tuple <PartitionKeyRangeServerBatchRequest, ArraySegment <ItemBatchOperation> >(request, pendingOperations)); }
internal virtual async Task <Tuple <PartitionKeyRangeServerBatchRequest, ArraySegment <ItemBatchOperation> > > CreateServerRequestAsync(CancellationToken cancellationToken) { // All operations should be for the same PKRange string partitionKeyRangeId = this.batchOperations[0].Context.PartitionKeyRangeId; ArraySegment <ItemBatchOperation> operationsArraySegment = new ArraySegment <ItemBatchOperation>(this.batchOperations.ToArray()); return(await PartitionKeyRangeServerBatchRequest.CreateAsync( partitionKeyRangeId, operationsArraySegment, this.maxBatchByteSize, this.maxBatchOperationCount, ensureContinuousOperationIndexes : false, serializerCore : this.serializerCore, cancellationToken : cancellationToken).ConfigureAwait(false)); }
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.PartitionKeyRangeId), 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)); } } }
public virtual async Task DispatchAsync( BatchPartitionMetric partitionMetric, CancellationToken cancellationToken = default) { this.interlockIncrementCheck.EnterLockCheck(); PartitionKeyRangeServerBatchRequest serverRequest = null; ArraySegment <ItemBatchOperation> pendingOperations; try { try { // HybridRow serialization might leave some pending operations out of the batch Tuple <PartitionKeyRangeServerBatchRequest, ArraySegment <ItemBatchOperation> > createRequestResponse = await this.CreateServerRequestAsync(cancellationToken); serverRequest = createRequestResponse.Item1; pendingOperations = createRequestResponse.Item2; // Any overflow goes to a new batch foreach (ItemBatchOperation operation in pendingOperations) { await this.retrier(operation, cancellationToken); } } catch (Exception ex) { // Exceptions happening during request creation, fail the entire list foreach (ItemBatchOperation itemBatchOperation in this.batchOperations) { itemBatchOperation.Context.Fail(this, ex); } throw; } try { Stopwatch stopwatch = Stopwatch.StartNew(); PartitionKeyRangeBatchExecutionResult result = await this.executor(serverRequest, cancellationToken); int numThrottle = result.ServerResponse.Any(r => r.StatusCode == (System.Net.HttpStatusCode)StatusCodes.TooManyRequests) ? 1 : 0; partitionMetric.Add( numberOfDocumentsOperatedOn: result.ServerResponse.Count, timeTakenInMilliseconds: stopwatch.ElapsedMilliseconds, numberOfThrottles: numThrottle); using (PartitionKeyRangeBatchResponse batchResponse = new PartitionKeyRangeBatchResponse(serverRequest.Operations.Count, result.ServerResponse, this.serializerCore)) { foreach (ItemBatchOperation itemBatchOperation in batchResponse.Operations) { TransactionalBatchOperationResult response = batchResponse[itemBatchOperation.OperationIndex]; // Bulk has diagnostics per a item operation. // Batch has a single diagnostics for the execute operation if (itemBatchOperation.DiagnosticsContext != null) { response.DiagnosticsContext = itemBatchOperation.DiagnosticsContext; response.DiagnosticsContext.AddDiagnosticsInternal(batchResponse.DiagnosticsContext); } else { response.DiagnosticsContext = batchResponse.DiagnosticsContext; } if (!response.IsSuccessStatusCode) { Documents.ShouldRetryResult shouldRetry = await itemBatchOperation.Context.ShouldRetryAsync(response, cancellationToken); if (shouldRetry.ShouldRetry) { await this.retrier(itemBatchOperation, cancellationToken); continue; } } itemBatchOperation.Context.Complete(this, response); } } } catch (Exception ex) { // Exceptions happening during execution fail all the Tasks part of the request (excluding overflow) foreach (ItemBatchOperation itemBatchOperation in serverRequest.Operations) { itemBatchOperation.Context.Fail(this, ex); } throw; } } catch (Exception ex) { DefaultTrace.TraceError("Exception during BatchAsyncBatcher: {0}", ex); } finally { this.batchOperations.Clear(); this.dispatched = true; } }
private static void AddHeadersToRequestMessage(RequestMessage requestMessage, PartitionKeyRangeServerBatchRequest partitionKeyRangeServerBatchRequest) { requestMessage.Headers.PartitionKeyRangeId = partitionKeyRangeServerBatchRequest.PartitionKeyRangeId; if (partitionKeyRangeServerBatchRequest.IsClientEncrypted) { requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsClientEncrypted, partitionKeyRangeServerBatchRequest.IsClientEncrypted.ToString()); requestMessage.Headers.Add(WFConstants.BackendHeaders.IntendedCollectionRid, partitionKeyRangeServerBatchRequest.IntendedCollectionRidValue); } requestMessage.Headers.Add(HttpConstants.HttpHeaders.ShouldBatchContinueOnError, bool.TrueString); requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchAtomic, bool.FalseString); requestMessage.Headers.Add(HttpConstants.HttpHeaders.IsBatchRequest, bool.TrueString); }
public virtual async Task DispatchAsync(CancellationToken cancellationToken = default(CancellationToken)) { this.interlockIncrementCheck.EnterLockCheck(); PartitionKeyRangeServerBatchRequest serverRequest = null; ArraySegment <ItemBatchOperation> pendingOperations; try { try { // HybridRow serialization might leave some pending operations out of the batch Tuple <PartitionKeyRangeServerBatchRequest, ArraySegment <ItemBatchOperation> > createRequestResponse = await this.CreateServerRequestAsync(cancellationToken); serverRequest = createRequestResponse.Item1; pendingOperations = createRequestResponse.Item2; // Any overflow goes to a new batch foreach (ItemBatchOperation operation in pendingOperations) { await this.retrier(operation, cancellationToken); } } catch (Exception ex) { // Exceptions happening during request creation, fail the entire list foreach (ItemBatchOperation itemBatchOperation in this.batchOperations) { itemBatchOperation.Context.Fail(this, ex); } throw; } try { PartitionKeyRangeBatchExecutionResult result = await this.executor(serverRequest, cancellationToken); if (result.IsSplit()) { foreach (ItemBatchOperation operationToRetry in result.Operations) { await this.retrier(operationToRetry, cancellationToken); } return; } using (PartitionKeyRangeBatchResponse batchResponse = new PartitionKeyRangeBatchResponse(serverRequest.Operations.Count, result.ServerResponse, this.cosmosSerializer)) { foreach (ItemBatchOperation itemBatchOperation in batchResponse.Operations) { BatchOperationResult response = batchResponse[itemBatchOperation.OperationIndex]; itemBatchOperation.Context.Complete(this, response); } } } catch (Exception ex) { // Exceptions happening during execution fail all the Tasks part of the request (excluding overflow) foreach (ItemBatchOperation itemBatchOperation in serverRequest.Operations) { itemBatchOperation.Context.Fail(this, ex); } throw; } } catch (Exception ex) { DefaultTrace.TraceError("Exception during BatchAsyncBatcher: {0}", ex); } finally { this.batchOperations.Clear(); this.dispached = true; } }