public async Task AvailableWorkers_IsNeverNegative() { // Arrange var options = new ThrottledOptions() { MaxConcurrency = 10, Logger = _logger, MessageMonitor = _monitor, StartTimeout = TimeSpan.FromSeconds(5), }; const int capacity = 10; var messageProcessingStrategy = new Throttled(options); var tcs = new TaskCompletionSource <object>(); // Act for (int i = 0; i < capacity; i++) { (await messageProcessingStrategy.StartWorkerAsync(() => tcs.Task, CancellationToken.None)).ShouldBeTrue(); messageProcessingStrategy.AvailableWorkers.ShouldBeGreaterThanOrEqualTo(0); } // Assert (await messageProcessingStrategy.StartWorkerAsync(() => tcs.Task, CancellationToken.None)).ShouldBeFalse(); messageProcessingStrategy.AvailableWorkers.ShouldBe(0); await AllowTasksToComplete(tcs); }
public async Task SimulatedListenLoop_ProcessedAllMessages_Sequentially( int numberOfMessagesToProcess, int concurrency) { var options = new ThrottledOptions() { MaxConcurrency = concurrency, Logger = Substitute.For <ILogger>(), MessageMonitor = Substitute.For <IMessageMonitor>(), StartTimeout = StartTimeout, ProcessMessagesSequentially = true, }; var messageProcessingStrategy = new Throttled(options); var counter = new ThreadSafeCounter(); var watch = Stopwatch.StartNew(); var actions = BuildFakeIncomingMessages(numberOfMessagesToProcess, counter); await ListenLoopExecuted(actions, messageProcessingStrategy); watch.Stop(); await Task.Delay(2000); counter.Count.ShouldBe(numberOfMessagesToProcess); }
public async Task WhenATaskCompletes_AvailableWorkersIsIncremented() { // Arrange var options = new ThrottledOptions() { MaxConcurrency = 3, Logger = _logger, MessageMonitor = _monitor, StartTimeout = TimeSpan.FromSeconds(5), }; var messageProcessingStrategy = new Throttled(options); var tcs = new TaskCompletionSource <object>(); // Act (await messageProcessingStrategy.StartWorkerAsync(() => tcs.Task, CancellationToken.None)).ShouldBeTrue(); // Assert messageProcessingStrategy.AvailableWorkers.ShouldBe(2); await AllowTasksToComplete(tcs); messageProcessingStrategy.MaxConcurrency.ShouldBe(3); messageProcessingStrategy.AvailableWorkers.ShouldBe(3); }
// ToDo: This should not be here. public SqsNotificationListener WithMaximumConcurrentLimitOnMessagesInFlightOf( int maximumAllowedMesagesInFlight, TimeSpan?startTimeout = null) { var options = new ThrottledOptions() { MaxConcurrency = maximumAllowedMesagesInFlight, StartTimeout = startTimeout ?? Timeout.InfiniteTimeSpan, Logger = _log, MessageMonitor = _messagingMonitor, }; _messageProcessingStrategy = new Throttled(options); return(this); }
public async Task ProcessMessagesSequentially_True_Processes_Messages_One_By_One() { // Arrange var options = new ThrottledOptions() { MaxConcurrency = 1, Logger = _logger, MessageMonitor = _monitor, StartTimeout = TimeSpan.FromSeconds(5), ProcessMessagesSequentially = true, }; var strategy = new Throttled(options); int count = 0; object syncRoot = new object(); Task DoWork() { if (Monitor.TryEnter(syncRoot)) { Interlocked.Increment(ref count); Monitor.Exit(syncRoot); } else { throw new InvalidOperationException("Failed to acquire lock as the thread was different."); } return(Task.CompletedTask); } // Act int loopCount = 100_000; Monitor.Enter(syncRoot); for (int i = 0; i < loopCount; i++) { await strategy.StartWorkerAsync(DoWork, CancellationToken.None); } Monitor.Exit(syncRoot); // Assert count.ShouldBe(loopCount); }
public void AvailableWorkers_StartsAtMaxConcurrency() { // Arrange var options = new ThrottledOptions() { MaxConcurrency = 123, Logger = _logger, MessageMonitor = _monitor, StartTimeout = TimeSpan.FromSeconds(5), }; // Act var messageProcessingStrategy = new Throttled(options); // Assert messageProcessingStrategy.AvailableWorkers.ShouldBe(123); }
public async Task Parallel_Processing_Does_Not_Exceed_Concurrency() { // Arrange int maxConcurrency = 10; var options = new ThrottledOptions() { MaxConcurrency = maxConcurrency, Logger = _logger, MessageMonitor = _monitor, StartTimeout = Timeout.InfiniteTimeSpan, ProcessMessagesSequentially = false, }; var strategy = new Throttled(options); long workDone = 0; int loopCount = 1_000; bool allWorkDone; using (var semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency)) { async Task DoWork() { if (!(await semaphore.WaitAsync(0))) { throw new InvalidOperationException("More workers are doing work than expected."); } Interlocked.Increment(ref workDone); semaphore.Release(); } // Act for (int i = 0; i < loopCount; i++) { await strategy.StartWorkerAsync(DoWork, CancellationToken.None); } allWorkDone = SpinWait.SpinUntil(() => Interlocked.Read(ref workDone) >= 1000, TimeSpan.FromSeconds(10)); } // Assert allWorkDone.ShouldBeTrue(); workDone.ShouldBe(loopCount); }
public async Task SimulatedListenLoop_WhenThrottlingOccurs_CallsMessageMonitor(int messageCount, int concurrency) { messageCount.ShouldBeGreaterThan(concurrency, "To cause throttling, message count must be greater than concurrency."); var fakeMonitor = Substitute.For <IMessageMonitor>(); var options = new ThrottledOptions() { MaxConcurrency = concurrency, Logger = Substitute.For <ILogger>(), MessageMonitor = fakeMonitor, StartTimeout = TimeSpan.FromTicks(1), }; var messageProcessingStrategy = new Throttled(options); var counter = new ThreadSafeCounter(); var tcs = new TaskCompletionSource <bool>(); for (int i = 0; i < concurrency; i++) { (await messageProcessingStrategy.StartWorkerAsync( async() => await tcs.Task, CancellationToken.None)).ShouldBeTrue(); } messageProcessingStrategy.AvailableWorkers.ShouldBe(0); for (int i = 0; i < messageCount - concurrency; i++) { (await messageProcessingStrategy.StartWorkerAsync(() => Task.CompletedTask, CancellationToken.None)).ShouldBeFalse(); } messageProcessingStrategy.AvailableWorkers.ShouldBe(0); tcs.SetResult(true); (await messageProcessingStrategy.WaitForAvailableWorkerAsync()).ShouldBeGreaterThan(0); fakeMonitor.Received().IncrementThrottlingStatistic(); fakeMonitor.Received().HandleThrottlingTime(Arg.Any <TimeSpan>()); }
public async Task SimulatedListenLoop_WhenThrottlingDoesNotOccur_CallsMessageMonitor_Once(int messageCount, int concurrency) { var fakeMonitor = Substitute.For <IMessageMonitor>(); var options = new ThrottledOptions() { MaxConcurrency = concurrency, Logger = Substitute.For <ILogger>(), MessageMonitor = fakeMonitor, StartTimeout = Timeout.InfiniteTimeSpan, }; var messageProcessingStrategy = new Throttled(options); var counter = new ThreadSafeCounter(); var actions = BuildFakeIncomingMessages(messageCount, counter); await ListenLoopExecuted(actions, messageProcessingStrategy); fakeMonitor.Received(1).IncrementThrottlingStatistic(); }
public async Task ProcessMessagesSequentially_False_Processes_Messages_In_Parallel() { // Arrange var options = new ThrottledOptions() { MaxConcurrency = 100, Logger = _logger, MessageMonitor = _monitor, StartTimeout = Timeout.InfiniteTimeSpan, ProcessMessagesSequentially = false, }; var strategy = new Throttled(options); long count = 0; var threadsSeen = new ConcurrentBag <int>(); Task DoWork() { threadsSeen.Add(Thread.CurrentThread.ManagedThreadId); Interlocked.Increment(ref count); return(Task.CompletedTask); } int loopCount = 1_000; // Act for (int i = 0; i < loopCount; i++) { await strategy.StartWorkerAsync(DoWork, CancellationToken.None); } bool allWorkDone = SpinWait.SpinUntil(() => Interlocked.Read(ref count) >= 1000, TimeSpan.FromSeconds(10)); // Assert allWorkDone.ShouldBeTrue(); count.ShouldBe(loopCount); threadsSeen.Distinct().Count().ShouldBeGreaterThan(1); }
public async Task SimulatedListenLoop_WhenThrottlingDoesNotOccur_DoNotCallMessageMonitor(int messageCount, int concurrency) { messageCount.ShouldBeLessThanOrEqualTo(concurrency, "To avoid throttling, message count must be not be greater than capacity."); var fakeMonitor = Substitute.For <IMessageMonitor>(); var options = new ThrottledOptions() { MaxConcurrency = concurrency, Logger = Substitute.For <ILogger>(), MessageMonitor = fakeMonitor, StartTimeout = Timeout.InfiniteTimeSpan, }; var messageProcessingStrategy = new Throttled(options); var counter = new ThreadSafeCounter(); var actions = BuildFakeIncomingMessages(messageCount, counter); await ListenLoopExecuted(actions, messageProcessingStrategy); fakeMonitor.DidNotReceive().IncrementThrottlingStatistic(); }