Example #1
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        // 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);
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        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();
        }
Example #10
0
        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();
        }