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); }
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 void SetUp() { _fakeMonitor = Substitute.For <IMessageMonitor>(); _fakeAmazonBatchSize = 10; _concurrencyLevel = 20; _messageProcessingStrategy = new Throttled(_concurrencyLevel, _fakeAmazonBatchSize, _fakeMonitor); _actionsProcessed = 0; }
public void SimulatedListenLoop_WhenThrottlingOccurs_CallsMessageMonitor() { var actions = BuildFakeIncomingMessages(50); _messageProcessingStrategy = new Throttled(20, _fakeAmazonBatchSize, _fakeMonitor); ListenLoopExecuted(actions); _fakeMonitor.Received().IncrementThrottlingStatistic(); }
public async Task WhenATasksIsAdded_AvailableWorkersIsDecremented() { var messageProcessingStrategy = new Throttled(123, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); messageProcessingStrategy.StartWorker(() => tcs.Task); messageProcessingStrategy.AvailableWorkers.ShouldBe(122); await AllowTasksToComplete(tcs); }
public async Task WhenATasksIsAdded_MaxWorkersIsUnaffected() { var messageProcessingStrategy = new Throttled(123, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); await messageProcessingStrategy.StartWorker(() => tcs.Task, CancellationToken.None); messageProcessingStrategy.MaxWorkers.ShouldBe(123); await AllowTasksToComplete(tcs); }
public async Task WhenATasksIsAdded_MaxWorkersIsUnaffected() { var messageProcessingStrategy = new Throttled(123, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); messageProcessingStrategy.StartWorker(() => tcs.Task); Assert.That(messageProcessingStrategy.MaxWorkers, Is.EqualTo(123)); await AllowTasksToComplete(tcs); }
public void ChangeMaxAllowedMessagesInFlightAtRuntime_TheChangeIsApplied() { var MaxAllowedMessagesInFlight = Substitute.For <Func <int> >(); MaxAllowedMessagesInFlight().Returns(100); _messageProcessingStrategy = new Throttled(MaxAllowedMessagesInFlight, 10, _fakeMonitor); MaxAllowedMessagesInFlight().Returns(90); Assert.That(_messageProcessingStrategy.BlockingThreshold, Is.EqualTo(90 - 10)); }
public async Task WhenATaskCompletes_AvailableWorkersIsIncremented() { var messageProcessingStrategy = new Throttled(3, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); await messageProcessingStrategy.StartWorker(() => tcs.Task, CancellationToken.None); messageProcessingStrategy.AvailableWorkers.ShouldBe(2); await AllowTasksToComplete(tcs); messageProcessingStrategy.MaxWorkers.ShouldBe(3); messageProcessingStrategy.AvailableWorkers.ShouldBe(3); }
public async Task SimulatedListenLoop_WhenThrottlingDoesNotOccur_DoNotCallMessageMonitor(int messageCount, int capacity) { Assert.That(messageCount, Is.LessThanOrEqualTo(capacity), "To avoid throttling, message count must be not be over capacity"); var fakeMonitor = Substitute.For <IMessageMonitor>(); var messageProcessingStrategy = new Throttled(capacity, fakeMonitor); var counter = new ThreadSafeCounter(); var actions = BuildFakeIncomingMessages(messageCount, counter); await ListenLoopExecuted(actions, messageProcessingStrategy); fakeMonitor.DidNotReceive().IncrementThrottlingStatistic(); }
public async Task <Stream> SecGetStreamAsync(string url) { Stream ToReturn = null; //Setup HttpClient hc = new HttpClient(); int havetried = 0; while (ToReturn == null && havetried < MaxTryCount) { //Prepare the request TryUpdateStatus("Preparing request..."); HttpRequestMessage req = PrepareHttpRequestMessage(); req.RequestUri = new Uri(url); req.Method = HttpMethod.Get; //Take the request delay timeout first TryUpdateStatus("Taking request delay..."); await Task.Delay(RequestDelay); //Make the call TryUpdateStatus("Attempting call..."); HttpResponseMessage resp = await hc.SendAsync(req); if (resp.StatusCode == HttpStatusCode.OK) { ToReturn = await resp.Content.ReadAsStreamAsync(); } else if (resp.StatusCode == HttpStatusCode.Forbidden) //Code 403 (throttled) { if (Throttled != null) //Raise the throttled event { Throttled.Invoke(); } TryUpdateStatus("Request was denied due to throttling. Waiting for timeout..."); await Task.Delay(TimeoutDelay); havetried = havetried + 1; TryUpdateStatus("Try count incremented and will try again."); } } //If the have tried is what caused it (it is over the limit), throw an exception if (havetried >= MaxTryCount) { throw new Exception("Unable to get data for URL '" + url + "'. Surpassed maximum try count of " + MaxTryCount.ToString()); } return(ToReturn); }
public async Task WhenATaskCompletes_AvailableWorkersIsIncremented() { var messageProcessingStrategy = new Throttled(3, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); messageProcessingStrategy.StartWorker(() => tcs.Task); Assert.That(messageProcessingStrategy.AvailableWorkers, Is.EqualTo(2)); await AllowTasksToComplete(tcs); Assert.That(messageProcessingStrategy.MaxWorkers, Is.EqualTo(3)); Assert.That(messageProcessingStrategy.AvailableWorkers, Is.EqualTo(3)); }
public async Task AvailableWorkers_CanReachZero() { const int capacity = 10; var messageProcessingStrategy = new Throttled(capacity, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); for (int i = 0; i < capacity; i++) { messageProcessingStrategy.StartWorker(() => tcs.Task); } messageProcessingStrategy.MaxWorkers.ShouldBe(capacity); messageProcessingStrategy.AvailableWorkers.ShouldBe(0); await AllowTasksToComplete(tcs); }
public async Task SimulatedListenLoop_WhenThrottlingOccurs_CallsMessageMonitor(int messageCount, int capacity) { Assert.That(messageCount, Is.GreaterThan(capacity), "To cause throttling, message count must be over capacity"); var fakeMonitor = Substitute.For <IMessageMonitor>(); var messageProcessingStrategy = new Throttled(capacity, fakeMonitor); var counter = new ThreadSafeCounter(); var actions = BuildFakeIncomingMessages(messageCount, counter); await ListenLoopExecuted(actions, messageProcessingStrategy); fakeMonitor.Received().IncrementThrottlingStatistic(); fakeMonitor.Received().HandleThrottlingTime(Arg.Any <long>()); }
public async Task AvailableWorkers_IsNeverNegative() { const int capacity = 10; var messageProcessingStrategy = new Throttled(capacity, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); for (int i = 0; i < capacity; i++) { messageProcessingStrategy.StartWorker(() => tcs.Task); messageProcessingStrategy.AvailableWorkers.ShouldBeGreaterThanOrEqualTo(0); } await AllowTasksToComplete(tcs); }
public void ChangeMaxWorkersAtRuntime_TheChangeIsApplied() { Func <int> maxAllowedMessagesInFlight = Substitute.For <Func <int> >(); maxAllowedMessagesInFlight().Returns(100); var messageProcessingStrategy = new Throttled(maxAllowedMessagesInFlight, _fakeMonitor); Assert.That(messageProcessingStrategy.MaxWorkers, Is.EqualTo(100)); Assert.That(messageProcessingStrategy.AvailableWorkers, Is.EqualTo(100)); maxAllowedMessagesInFlight().Returns(90); Assert.That(messageProcessingStrategy.MaxWorkers, Is.EqualTo(90)); Assert.That(messageProcessingStrategy.AvailableWorkers, Is.EqualTo(90)); }
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 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 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 AvailableWorkers_CanGoToZeroAndBackToFull() { const int capacity = 10; var messageProcessingStrategy = new Throttled(capacity, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); for (int i = 0; i < capacity; i++) { messageProcessingStrategy.StartWorker(() => tcs.Task); } Assert.That(messageProcessingStrategy.AvailableWorkers, Is.EqualTo(0)); await AllowTasksToComplete(tcs); Assert.That(messageProcessingStrategy.AvailableWorkers, Is.EqualTo(capacity)); }
public async Task AvailableWorkers_CanGoToZeroAndBackToFull() { const int capacity = 10; var messageProcessingStrategy = new Throttled(capacity, _fakeMonitor); var tcs = new TaskCompletionSource <object>(); for (int i = 0; i < capacity; i++) { await messageProcessingStrategy.StartWorker(() => tcs.Task, CancellationToken.None); } messageProcessingStrategy.AvailableWorkers.ShouldBe(0); await AllowTasksToComplete(tcs); messageProcessingStrategy.AvailableWorkers.ShouldBe(capacity); }
public MarketStackService(IMapper mapper, IOptions <MarketStackOptions> options, HttpClient httpClient) { _mapper = mapper; _options = options.Value; if (_options.MaxRequestsPerSecond >= 10) { _throttled = new Throttled(_options.MaxRequestsPerSecond / 10, 100); } else { _throttled = new Throttled(_options.MaxRequestsPerSecond, 1000); } _httpClient = httpClient; _httpClient.Timeout = TimeSpan.FromMinutes(10); _apiUrl = _options.Https ? "https://api.marketstack.com/v1" : "http://api.marketstack.com/v1"; }
public MarketstackService(IOptions <MarketstackOptions> options, HttpClient httpClient, ILogger <MarketstackService> logger) { _options = options.Value; if (_options.MaxRequestsPerSecond >= 10) { _throttled = new Throttled(_options.MaxRequestsPerSecond / 10, 100); } else { _throttled = new Throttled(_options.MaxRequestsPerSecond, 1000); } _httpClient = httpClient; _httpClient.Timeout = TimeSpan.FromMinutes(10); _apiUrl = options.Value.Https ? "https://api.marketstack.com/v1" : "http://api.marketstack.com/v1"; _logger = logger; }
public async Task SimulatedListenLoop_ProcessedAllMessages(int numberOfMessagesToProcess) { var fakeMonitor = Substitute.For <IMessageMonitor>(); var messageProcessingStrategy = new Throttled(ConcurrencyLevel, fakeMonitor); var counter = new ThreadSafeCounter(); var watch = new Stopwatch(); watch.Start(); var actions = BuildFakeIncomingMessages(numberOfMessagesToProcess, counter); await ListenLoopExecuted(actions, messageProcessingStrategy); watch.Stop(); await Task.Delay(2000); counter.Count.ShouldBe(numberOfMessagesToProcess); }
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 Run_ExecutionCounterInRange(int taskLimit, int limitingPeriodInMilliseconds, int testPeriodRounds) { Throttled throttled = new Throttled(taskLimit, limitingPeriodInMilliseconds); Stopwatch stopwatch = new Stopwatch(); int testPeriodInMilliseconds = limitingPeriodInMilliseconds * testPeriodRounds; int expectedCounter = taskLimit * testPeriodRounds; int counter = 0; stopwatch.Start(); while (stopwatch.Elapsed < TimeSpan.FromMilliseconds(testPeriodInMilliseconds)) { await throttled.Run(() => Task.CompletedTask); if (stopwatch.Elapsed < TimeSpan.FromMilliseconds(testPeriodInMilliseconds)) { Interlocked.Increment(ref counter); } } Assert.Equal(expectedCounter, counter); }
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(); }