public async Task TestDelayedQueueTaskAsync() { ComparableTaskScheduler scheduler = new ComparableTaskScheduler(); Task task = new Task(() => { Assert.AreEqual(1, scheduler.CurrentRunningTaskCount); }); Task delayedTask = new Task(() => { Assert.AreEqual(1, scheduler.CurrentRunningTaskCount); }); Assert.AreEqual(true, scheduler.TryQueueTask(new TestComparableTask(0, delayedTask), TimeSpan.FromMilliseconds(200))); Assert.AreEqual(false, scheduler.TryQueueTask(new TestComparableTask(0, delayedTask), TimeSpan.FromMilliseconds(200))); Assert.AreEqual(false, scheduler.TryQueueTask(new TestComparableTask(0, task))); Assert.AreEqual(true, scheduler.TryQueueTask(new TestComparableTask(1, task))); await Task.Delay(150); Assert.AreEqual(true, task.IsCompleted); Assert.AreEqual(false, delayedTask.IsCompleted); Assert.AreEqual(0, scheduler.CurrentRunningTaskCount); await Task.Delay(200); Assert.AreEqual(true, delayedTask.IsCompleted); }
/// <summary> /// Initializes a new instance of the CosmosCrossPartitionQueryExecutionContext class. /// </summary> /// <param name="queryContext">Constructor parameters for the base class.</param> /// <param name="maxConcurrency">The max concurrency</param> /// <param name="maxBufferedItemCount">The max buffered item count</param> /// <param name="maxItemCount">Max item count</param> /// <param name="moveNextComparer">Comparer used to figure out that document producer tree to serve documents from next.</param> /// <param name="fetchPrioirtyFunction">The priority function to determine which partition to fetch documents from next.</param> /// <param name="equalityComparer">Used to determine whether we need to return the continuation token for a partition.</param> /// <param name="returnResultsInDeterministicOrder">Whether or not to return results in deterministic order.</param> /// <param name="testSettings">Test settings.</param> protected CosmosCrossPartitionQueryExecutionContext( CosmosQueryContext queryContext, int?maxConcurrency, int?maxItemCount, int?maxBufferedItemCount, IComparer <ItemProducerTree> moveNextComparer, Func <ItemProducerTree, int> fetchPrioirtyFunction, IEqualityComparer <CosmosElement> equalityComparer, bool returnResultsInDeterministicOrder, TestInjections testSettings) { if (moveNextComparer == null) { throw new ArgumentNullException(nameof(moveNextComparer)); } this.queryContext = queryContext ?? throw new ArgumentNullException(nameof(queryContext)); this.queryClient = queryContext.QueryClient ?? throw new ArgumentNullException(nameof(queryContext.QueryClient)); this.itemProducerForest = new PriorityQueue <ItemProducerTree>(moveNextComparer, isSynchronized: true); this.fetchPrioirtyFunction = fetchPrioirtyFunction ?? throw new ArgumentNullException(nameof(fetchPrioirtyFunction)); this.comparableTaskScheduler = new ComparableTaskScheduler(maxConcurrency.GetValueOrDefault(0)); this.equalityComparer = equalityComparer ?? throw new ArgumentNullException(nameof(equalityComparer)); this.testSettings = testSettings; this.requestChargeTracker = new RequestChargeTracker(); this.diagnosticsPages = new ConcurrentBag <QueryPageDiagnostics>(); this.actualMaxPageSize = maxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxPageSize < 0) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be greater than int.MaxValue"); } if (maxBufferedItemCount.HasValue) { this.actualMaxBufferedItemCount = maxBufferedItemCount.Value; } else { this.actualMaxBufferedItemCount = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } if (this.actualMaxBufferedItemCount < 0) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } this.CanPrefetch = maxConcurrency.HasValue && maxConcurrency.Value != 0; this.returnResultsInDeterministicOrder = returnResultsInDeterministicOrder; }
/// <summary> /// Initializes a new instance of the CosmosCrossPartitionQueryExecutionContext class. /// </summary> /// <param name="initParams">Constructor parameters for the base class.</param> /// <param name="moveNextComparer">Comparer used to figure out that document producer tree to serve documents from next.</param> /// <param name="fetchPrioirtyFunction">The priority function to determine which partition to fetch documents from next.</param> /// <param name="equalityComparer">Used to determine whether we need to return the continuation token for a partition.</param> protected CosmosCrossPartitionQueryExecutionContext( CosmosQueryContext initParams, IComparer <ItemProducerTree> moveNextComparer, Func <ItemProducerTree, int> fetchPrioirtyFunction, IEqualityComparer <CosmosElement> equalityComparer) { if (moveNextComparer == null) { throw new ArgumentNullException(nameof(moveNextComparer)); } if (fetchPrioirtyFunction == null) { throw new ArgumentNullException(nameof(fetchPrioirtyFunction)); } if (equalityComparer == null) { throw new ArgumentNullException(nameof(equalityComparer)); } this.queryContext = initParams; this.queryRequestOptions = initParams.QueryRequestOptions; this.itemProducerForest = new PriorityQueue <ItemProducerTree>(moveNextComparer, isSynchronized: true); this.fetchPrioirtyFunction = fetchPrioirtyFunction; this.comparableTaskScheduler = new ComparableTaskScheduler(initParams.QueryRequestOptions.MaxConcurrency.GetValueOrDefault(0)); this.equalityComparer = equalityComparer; this.requestChargeTracker = new RequestChargeTracker(); this.partitionedQueryMetrics = new ConcurrentBag <Tuple <string, QueryMetrics> >(); this.actualMaxPageSize = this.queryRequestOptions.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxPageSize < 0) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be greater than int.MaxValue"); } if (this.queryRequestOptions.MaxBufferedItemCount.HasValue) { this.actualMaxBufferedItemCount = this.queryRequestOptions.MaxBufferedItemCount.Value; } else { this.actualMaxBufferedItemCount = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } if (this.actualMaxBufferedItemCount < 0) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } }
public async Task TestOperationCanceledExceptionAsync() { int seed = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; Random rand = new Random(seed); IDocumentClientRetryPolicy retryPolicy = new MockRetryPolicy(rand); ComparableTaskScheduler scheduler = new ComparableTaskScheduler(1); DocumentProducer <int> producer = new DocumentProducer <int>( scheduler, (continuation, pageSize) => null, new PartitionKeyRange { Id = "test", MinInclusive = "", MaxExclusive = "ff" }, p => 0, (request, token) => { scheduler.Stop(); throw new Exception(); }, () => retryPolicy, (produer, size, ru, queryMetrics, token, length) => { }, Guid.NewGuid() ) { PageSize = 1000 }; await producer.MoveNextAsync(new CancellationTokenSource().Token); }
public void TestStop() { using ComparableTaskScheduler scheduler = new ComparableTaskScheduler(); Assert.AreEqual(true, scheduler.TryQueueTask(new TestComparableTask(0, Task.FromResult(false)))); scheduler.Stop(); Assert.AreEqual(false, scheduler.TryQueueTask(new TestComparableTask(0, Task.FromResult(false)))); }
public async Task SimpleTestAsync() { foreach (bool useConstructorToAddTasks in new[] { true, false }) { List <Task> tasks = new List <Task>(); int maximumConcurrencyLevel = 10; for (int i = 0; i < maximumConcurrencyLevel; ++i) { tasks.Add(new Task(() => { })); } await Task.Delay(1); foreach (Task task in tasks) { Assert.AreEqual(false, task.IsCompleted); } ComparableTaskScheduler scheduler = null; try { if (useConstructorToAddTasks) { scheduler = new ComparableTaskScheduler( tasks.Select(task => new TestComparableTask(tasks.IndexOf(task), task)), maximumConcurrencyLevel); } else { scheduler = new ComparableTaskScheduler(maximumConcurrencyLevel); for (int i = 0; i < maximumConcurrencyLevel; ++i) { Assert.AreEqual(true, scheduler.TryQueueTask(new TestComparableTask(i, tasks[i]))); } } bool completionStatus = Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(10)); Assert.IsTrue(completionStatus); foreach (Task task in tasks) { Assert.AreEqual(true, task.IsCompleted, $"Is overloaded constructor {useConstructorToAddTasks} and status {task.Status.ToString()}"); } } finally { if (scheduler != null) { scheduler.Dispose(); } } } }
public void TestMaximumConcurrencyLevel() { using ComparableTaskScheduler firstScheduler = new ComparableTaskScheduler(10); Assert.AreEqual(10, firstScheduler.MaximumConcurrencyLevel); using ComparableTaskScheduler scheduler = new ComparableTaskScheduler(); Assert.AreEqual(Environment.ProcessorCount, scheduler.MaximumConcurrencyLevel); scheduler.IncreaseMaximumConcurrencyLevel(1); Assert.AreEqual(Environment.ProcessorCount + 1, scheduler.MaximumConcurrencyLevel); try { scheduler.IncreaseMaximumConcurrencyLevel(-1); Assert.Fail("Expect ArgumentOutOfRangeException"); } catch (ArgumentOutOfRangeException) { } }
/// <summary> /// Initializes a new instance of the CrossPartitionQueryExecutionContext class. /// </summary> /// <param name="initParams">Constructor parameters for the base class.</param> /// <param name="rewrittenQuery"> /// Queries will get rewritten for different reasons. /// You can read more about this in the details from the concrete classes. /// </param> /// <param name="moveNextComparer">Comparer used to figure out that document producer tree to serve documents from next.</param> /// <param name="fetchPrioirtyFunction">The priority function to determine which partition to fetch documents from next.</param> /// <param name="equalityComparer">Used to determine whether we need to return the continuation token for a partition.</param> protected CrossPartitionQueryExecutionContext( DocumentQueryExecutionContextBase.InitParams initParams, string rewrittenQuery, IComparer <DocumentProducerTree> moveNextComparer, Func <DocumentProducerTree, int> fetchPrioirtyFunction, IEqualityComparer <CosmosElement> equalityComparer) : base(initParams) { if (!string.IsNullOrWhiteSpace(rewrittenQuery)) { this.querySpec = new SqlQuerySpec(rewrittenQuery, this.QuerySpec.Parameters); } if (moveNextComparer == null) { throw new ArgumentNullException($"{nameof(moveNextComparer)} can not be null"); } if (fetchPrioirtyFunction == null) { throw new ArgumentNullException($"{nameof(fetchPrioirtyFunction)} can not be null"); } if (equalityComparer == null) { throw new ArgumentNullException($"{nameof(equalityComparer)} can not be null"); } this.documentProducerForest = new PriorityQueue <DocumentProducerTree>(moveNextComparer, isSynchronized: true); this.fetchPrioirtyFunction = fetchPrioirtyFunction; this.comparableTaskScheduler = new ComparableTaskScheduler(initParams.FeedOptions.MaxDegreeOfParallelism); this.equalityComparer = equalityComparer; this.requestChargeTracker = new RequestChargeTracker(); this.partitionedQueryMetrics = new ConcurrentBag <Tuple <string, QueryMetrics> >(); this.actualMaxPageSize = this.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxPageSize < 0) { throw new OverflowException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new OverflowException("actualMaxPageSize should never be greater than int.MaxValue"); } if (IsMaxBufferedItemCountSet(this.MaxBufferedItemCount)) { this.actualMaxBufferedItemCount = this.MaxBufferedItemCount; } else { this.actualMaxBufferedItemCount = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } if (this.actualMaxBufferedItemCount < 0) { throw new OverflowException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new OverflowException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } }
public DocumentProducer( ComparableTaskScheduler taskScheduler, Func <string, int, DocumentServiceRequest> createRequestFunc, PartitionKeyRange targetRange, Func <DocumentProducer <T>, int> taskPriorityFunc, Func <DocumentServiceRequest, CancellationToken, Task <FeedResponse <T> > > executeRequestFunc, Func <IDocumentClientRetryPolicy> createRetryPolicyFunc, ProduceAsyncCompleteDelegate produceAsyncCompleteCallback, Guid correlatedActivityId, long initialPageSize = 50, string initialContinuationToken = null) { if (taskScheduler == null) { throw new ArgumentNullException("taskScheduler"); } if (createRequestFunc == null) { throw new ArgumentNullException("documentServiceRequest"); } if (targetRange == null) { throw new ArgumentNullException("targetRange"); } if (taskPriorityFunc == null) { throw new ArgumentNullException("taskPriorityFunc"); } if (executeRequestFunc == null) { throw new ArgumentNullException("executeRequestFunc"); } if (createRetryPolicyFunc == null) { throw new ArgumentNullException("createRetryPolicyFunc"); } if (produceAsyncCompleteCallback == null) { throw new ArgumentNullException("produceAsyncCallback"); } this.taskScheduler = taskScheduler; this.itemBuffer = new AsyncCollection <FetchResult>(); this.createRequestFunc = createRequestFunc; this.targetRange = targetRange; this.taskPriorityFunc = taskPriorityFunc; this.createRetryPolicyFunc = createRetryPolicyFunc; this.executeRequestFunc = executeRequestFunc; this.produceAsyncCompleteCallback = produceAsyncCompleteCallback; this.PageSize = initialPageSize; if ((int)this.PageSize < 0) { throw new ArithmeticException("page size is negative.."); } this.correlatedActivityId = correlatedActivityId; this.CurrentBackendContinuationToken = initialContinuationToken; this.moveNextSchedulingMetrics = new SchedulingStopwatch(); this.moveNextSchedulingMetrics.Ready(); this.fetchSchedulingMetrics = new SchedulingStopwatch(); this.fetchSchedulingMetrics.Ready(); this.fetchExecutionRangeAccumulator = new FetchExecutionRangeAccumulator(this.targetRange.Id); this.fetchStateSemaphore = new SemaphoreSlim(1, 1); }
public async Task ConcurrentMoveNextTryScheduleTestAsync() { int seed = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; Random rand = new Random(seed); int maxValue = 100; int trials = 1000; int maxTicks = 100; IEnumerable <int> expectedValues = Enumerable.Range(1, maxValue); IDocumentClientRetryPolicy retryPolicy = new MockRetryPolicy(rand); ComparableTaskScheduler scheduler = new ComparableTaskScheduler(1); for (int trial = 0; trial < trials; ++trial) { DocumentProducer <int> producer = new DocumentProducer <int>( scheduler, (continuation, pageSize) => DocumentServiceRequest.Create( OperationType.Query, "/dbs/db/colls/coll", ResourceType.Document, new MemoryStream(Encoding.UTF8.GetBytes(continuation)), AuthorizationTokenType.PrimaryMasterKey, new StringKeyValueCollection { { HttpConstants.HttpHeaders.Continuation, continuation } }), new PartitionKeyRange { Id = "test", MinInclusive = "", MaxExclusive = "ff" }, p => 0, (request, token) => { if (rand.Next(4) == 0) { throw new Exception(); } if (rand.Next(10) == 0) { return(Task.FromResult(new FeedResponse <int>(new int[] { }, 0, request.Headers))); } using (StreamReader reader = new StreamReader(request.Body)) { int value = int.Parse(reader.ReadToEnd()) + 1; INameValueCollection headers = new StringKeyValueCollection { { HttpConstants.HttpHeaders.Continuation, value >= maxValue? null : value.ToString(CultureInfo.InvariantCulture) } }; return(Task.FromResult(new FeedResponse <int>(new int[] { value }, 1, headers))); } }, () => retryPolicy, (produer, size, ru, queryMetrics, token, length) => { }, Guid.NewGuid(), 1000, "0"); Timer timer = new Timer( (state) => producer.TryScheduleFetch(TimeSpan.FromTicks(rand.Next(maxTicks))), null, TimeSpan.FromTicks(rand.Next(maxTicks)), TimeSpan.FromTicks(rand.Next(maxTicks))); List <int> actualValues = new List <int>(); CancellationTokenSource tokenSource = new CancellationTokenSource(5000); while (await producer.MoveNextAsync(tokenSource.Token)) { actualValues.Add(producer.Current); } Assert.AreEqual( string.Join(", ", expectedValues), string.Join(", ", actualValues), string.Format(CultureInfo.InvariantCulture, "seed: {0}", seed)); } }
protected ParallelDocumentQueryExecutionContextBase( IDocumentQueryClient client, ResourceType resourceTypeEnum, Type resourceType, Expression expression, FeedOptions feedOptions, string resourceLink, string rewrittenQuery, Guid correlatedActivityId, bool isContinuationExpected, bool getLazyFeedResponse, bool isDynamicPageSizeAllowed) : base( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, getLazyFeedResponse, correlatedActivityId) { this.DocumentProducers = new List <DocumentProducer <T> >(); this.chargeTracker = new RequestChargeTracker(); this.groupedQueryMetrics = new Dictionary <string, QueryMetrics>(); this.partitionedQueryMetrics = new ConcurrentBag <Tuple <string, QueryMetrics> >(); this.responseHeaders = new StringKeyValueCollection(); this.actualMaxBufferedItemCount = Math.Max(this.MaxBufferedItemCount, ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize); this.currentAverageNumberOfRequestsPerTask = 1d; if (!string.IsNullOrEmpty(rewrittenQuery)) { this.querySpec = new SqlQuerySpec(rewrittenQuery, this.QuerySpec.Parameters); } this.TaskScheduler = new ComparableTaskScheduler(this.GetCurrentMaximumAllowedConcurrentTasks(0)); this.ShouldPrefetch = feedOptions.MaxDegreeOfParallelism != 0; this.IsContinuationExpected = isContinuationExpected; this.DefaultContinuationToken = Guid.NewGuid().ToString(); this.InitializationSchedulingMetrics = new SchedulingStopwatch(); this.InitializationSchedulingMetrics.Ready(); this.CurrentContinuationTokens = new SortedList <DocumentProducer <T>, string>( Comparer <DocumentProducer <T> > .Create((producer1, producer2) => string.CompareOrdinal(producer1.TargetRange.MinInclusive, producer2.TargetRange.MinInclusive))); this.actualMaxPageSize = this.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxBufferedItemCount < 0) { throw new OverflowException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new OverflowException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } if (this.actualMaxPageSize < 0) { throw new OverflowException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new OverflowException("actualMaxPageSize should never be greater than int.MaxValue"); } }