protected override async Task <DocumentFeedResponse <CosmosElement> > ExecuteInternalAsync(CancellationToken token) { CollectionCache collectionCache = await this.Client.GetCollectionCacheAsync(); PartitionKeyRangeCache partitionKeyRangeCache = await this.Client.GetPartitionKeyRangeCacheAsync(); IDocumentClientRetryPolicy retryPolicyInstance = this.Client.ResetSessionTokenRetryPolicy.GetRequestPolicy(); retryPolicyInstance = new InvalidPartitionExceptionRetryPolicy(retryPolicyInstance); if (base.ResourceTypeEnum.IsPartitioned()) { retryPolicyInstance = new PartitionKeyRangeGoneRetryPolicy( collectionCache, partitionKeyRangeCache, PathsHelper.GetCollectionPath(base.ResourceLink), retryPolicyInstance); } return(await BackoffRetryUtility <DocumentFeedResponse <CosmosElement> > .ExecuteAsync( async() => { this.fetchExecutionRangeAccumulator.BeginFetchRange(); ++this.retries; Tuple <DocumentFeedResponse <CosmosElement>, string> responseAndPartitionIdentifier = await this.ExecuteOnceAsync(retryPolicyInstance, token); DocumentFeedResponse <CosmosElement> response = responseAndPartitionIdentifier.Item1; string partitionIdentifier = responseAndPartitionIdentifier.Item2; if (!string.IsNullOrEmpty(response.ResponseHeaders[HttpConstants.HttpHeaders.QueryMetrics])) { this.fetchExecutionRangeAccumulator.EndFetchRange( partitionIdentifier, response.ActivityId, response.Count, this.retries); response = new DocumentFeedResponse <CosmosElement>( response, response.Count, response.Headers, response.UseETagAsContinuation, new Dictionary <string, QueryMetrics> { { partitionIdentifier, QueryMetrics.CreateFromDelimitedStringAndClientSideMetrics( response.ResponseHeaders[HttpConstants.HttpHeaders.QueryMetrics], response.ResponseHeaders[HttpConstants.HttpHeaders.IndexUtilization], new ClientSideMetrics( this.retries, response.RequestCharge, this.fetchExecutionRangeAccumulator.GetExecutionRanges())) } }, response.RequestStatistics, response.DisallowContinuationTokenMessage, response.ResponseLengthBytes); } this.retries = -1; return response; }, retryPolicyInstance, token)); }
protected override async Task <FeedResponse <dynamic> > ExecuteInternalAsync(CancellationToken cancellationToken) { CollectionCache collectionCache = await this.Client.GetCollectionCacheAsync(); PartitionKeyRangeCache partitionKeyRangeCache = await this.Client.GetPartitionKeyRangeCache(); IDocumentClientRetryPolicy retryPolicyInstance = this.Client.RetryPolicy.GetRequestPolicy(); retryPolicyInstance = new InvalidPartitionExceptionRetryPolicy(collectionCache, retryPolicyInstance); if (base.ResourceTypeEnum.IsPartitioned()) { retryPolicyInstance = new PartitionKeyRangeGoneRetryPolicy( collectionCache, partitionKeyRangeCache, PathsHelper.GetCollectionPath(base.ResourceLink), retryPolicyInstance); } return(await BackoffRetryUtility <FeedResponse <dynamic> > .ExecuteAsync( async() => { this.fetchExecutionRangeAccumulator.BeginFetchRange(); ++this.retries; this.fetchSchedulingMetrics.Start(); this.fetchExecutionRangeAccumulator.BeginFetchRange(); FeedResponse <dynamic> response = await this.ExecuteOnceAsync(retryPolicyInstance, cancellationToken); this.fetchSchedulingMetrics.Stop(); this.fetchExecutionRangeAccumulator.EndFetchRange(response.Count, this.retries); if (!string.IsNullOrEmpty(response.Headers[HttpConstants.HttpHeaders.QueryMetrics])) { this.fetchExecutionRangeAccumulator.EndFetchRange(response.Count, this.retries); response = new FeedResponse <dynamic>( response, response.Count, response.Headers, response.UseETagAsContinuation, new Dictionary <string, QueryMetrics> { { singlePartitionKeyId, QueryMetrics.CreateFromDelimitedStringAndClientSideMetrics( response.Headers[HttpConstants.HttpHeaders.QueryMetrics], new ClientSideMetrics( this.retries, response.RequestCharge, this.fetchExecutionRangeAccumulator.GetExecutionRanges(), string.IsNullOrEmpty(response.ResponseContinuation) ? new List <Tuple <string, SchedulingTimeSpan> >() { new Tuple <string, SchedulingTimeSpan>(singlePartitionKeyId, this.fetchSchedulingMetrics.Elapsed) } : new List <Tuple <string, SchedulingTimeSpan> >()), Guid.Parse(response.ActivityId)) } }, response.RequestStatistics, response.DisallowContinuationTokenMessage, response.ResponseLengthBytes); } this.retries = -1; return response; }, retryPolicyInstance, cancellationToken)); }
/// <summary> /// Buffers more documents in the producer. /// </summary> /// <param name="token">The cancellation token.</param> /// <returns>A task to await on.</returns> public async Task BufferMoreDocumentsAsync(CancellationToken token) { token.ThrowIfCancellationRequested(); try { await this.fetchSemaphore.WaitAsync(); if (!this.HasMoreBackendResults) { // Just NOP return; } this.fetchSchedulingMetrics.Start(); this.fetchExecutionRangeAccumulator.BeginFetchRange(); int pageSize = (int)Math.Min(this.pageSize, (long)int.MaxValue); using (DocumentServiceRequest request = this.createRequestFunc(this.PartitionKeyRange, this.backendContinuationToken, pageSize)) { IDocumentClientRetryPolicy retryPolicy = this.createRetryPolicyFunc(); // Custom backoff and retry while (true) { int retries = 0; try { DocumentFeedResponse <CosmosElement> feedResponse = await this.executeRequestFunc(request, retryPolicy, token); this.fetchExecutionRangeAccumulator.EndFetchRange( this.PartitionKeyRange.Id, feedResponse.ActivityId, feedResponse.Count, retries); this.fetchSchedulingMetrics.Stop(); this.hasStartedFetching = true; this.backendContinuationToken = feedResponse.ResponseContinuation; this.activityId = Guid.Parse(feedResponse.ActivityId); await this.bufferedPages.AddAsync(TryMonad <DocumentFeedResponse <CosmosElement> > .FromResult(feedResponse)); Interlocked.Add(ref this.bufferedItemCount, feedResponse.Count); QueryMetrics queryMetrics = QueryMetrics.Zero; if (feedResponse.ResponseHeaders[HttpConstants.HttpHeaders.QueryMetrics] != null) { queryMetrics = QueryMetrics.CreateFromDelimitedStringAndClientSideMetrics( feedResponse.ResponseHeaders[HttpConstants.HttpHeaders.QueryMetrics], new ClientSideMetrics( retries, feedResponse.RequestCharge, this.fetchExecutionRangeAccumulator.GetExecutionRanges(), new List <Tuple <string, SchedulingTimeSpan> >())); } if (!this.HasMoreBackendResults) { queryMetrics = QueryMetrics.CreateWithSchedulingMetrics( queryMetrics, new List <Tuple <string, SchedulingTimeSpan> > { new Tuple <string, SchedulingTimeSpan>( this.PartitionKeyRange.Id, this.fetchSchedulingMetrics.Elapsed) }); } this.produceAsyncCompleteCallback( this, feedResponse.Count, feedResponse.RequestCharge, queryMetrics, feedResponse.ResponseLengthBytes, token); break; } catch (Exception exception) { // See if we need to retry or just throw ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(exception, token); if (!shouldRetryResult.ShouldRetry) { Exception exceptionToBuffer; if (shouldRetryResult.ExceptionToThrow != null) { exceptionToBuffer = shouldRetryResult.ExceptionToThrow; } else { // Propagate original exception. exceptionToBuffer = exception; } // Buffer the exception instead of throwing, since we don't want an unobserved exception. await this.bufferedPages.AddAsync(TryMonad <DocumentFeedResponse <CosmosElement> > .FromException(exceptionToBuffer)); // null out the backend continuation token, // so that people stop trying to buffer more on this producer. this.hasStartedFetching = true; this.backendContinuationToken = null; break; } else { await Task.Delay(shouldRetryResult.BackoffTime); retries++; } } } } } finally { this.fetchSchedulingMetrics.Stop(); this.fetchSemaphore.Release(); } }
/// <summary> /// Creates a new QueryMetrics from the backend delimited string. /// </summary> /// <param name="delimitedString">The backend delimited string to deserialize from.</param> /// <param name="indexUtilization">The index utilization from the backend</param> /// <returns>A new QueryMetrics from the backend delimited string.</returns> internal static QueryMetrics CreateFromDelimitedString(string delimitedString, string indexUtilization) { return(QueryMetrics.CreateFromDelimitedStringAndClientSideMetrics(delimitedString, indexUtilization, new ClientSideMetrics(0, 0, new List <FetchExecutionRange>()))); }