public async Task Pushing_for_slots_after_start_works_when_batch_size_is_reached()
        {
            var receivedItems = new ConcurrentQueue <List <int> >[4]
            {
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
            };

            var countDownEvent = new CountdownEvent(16);

            // choose insanely high push interval
            var completion = new MultiProducerConcurrentCompletion <int>(batchSize: 100, pushInterval: TimeSpan.FromDays(1), maxConcurrency: 4, numberOfSlots: 4);

            completion.Start((items, slot, state, token) =>
            {
                receivedItems[slot].Enqueue(new List <int>(items)); // take a copy
                if (!countDownEvent.IsSet)
                {
                    countDownEvent.Signal();
                }
                return(Task.FromResult(0));
            });

            var numberOfItems = await PushConcurrentlyTwoThousandItemsInPackagesOfFiveHundredIntoFourSlots(completion);

            // we wait for 16 counts and then complete midway
            await Task.Run(() => countDownEvent.Wait(TimeSpan.FromSeconds(5)));

            await completion.Complete();

            Assert.AreEqual(TriangularNumber(numberOfItems), Flatten(receivedItems).Sum(i => i));
        }
        public async Task Complete_works_even_when_push_interval_and_batch_size_not_reached()
        {
            var receivedItems = new ConcurrentQueue <List <int> >[4]
            {
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
            };

            // choose insanely high batchSize and pushInterval to force the loop hanging
            var completion = new MultiProducerConcurrentCompletion <int>(batchSize: 10000, pushInterval: TimeSpan.FromDays(1), maxConcurrency: 4, numberOfSlots: 4);

            completion.Start((items, slot, state, token) =>
            {
                receivedItems[slot].Enqueue(new List <int>(items)); // take a copy
                return(Task.FromResult(0));
            });

            var numberOfItems = await PushConcurrentlyTwoThousandItemsInPackagesOfFiveHundredIntoFourSlots(completion);

            await completion.Complete();

            var sumOfAllElementsSeenSoFar = Flatten(receivedItems).Sum(i => i);

            Assert.AreEqual(TriangularNumber(numberOfItems), sumOfAllElementsSeenSoFar);
            Assert.AreEqual(TriangularNumber(numberOfItems), Flatten(receivedItems).Sum(i => i), "Dispatcher complete brought more items than expected");
        }
        public async Task Pushing_and_complete_with_no_drain_without_start_empties_slots()
        {
            var pushedItems = new ConcurrentQueue <List <int> >[4]
            {
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
            };

            // choose insanely high batchSize to force push interval picking up all the content
            var completion = new MultiProducerConcurrentCompletion <int>(batchSize: 100, pushInterval: TimeSpan.FromMilliseconds(1), maxConcurrency: 4, numberOfSlots: 4);

            await PushConcurrentlyTwoThousandItemsInPackagesOfFiveHundredIntoFourSlots(completion);

            await completion.Complete(drain : false);

            var countDownEvent = new CountdownEvent(1);

            completion.Start((items, slot, state, token) =>
            {
                pushedItems[slot].Enqueue(new List <int>(items)); // take a copy
                countDownEvent.Signal();
                return(Task.FromResult(0));
            });

            completion.Push(1, slotNumber: 1);

            await Task.Run(() => countDownEvent.Wait(TimeSpan.FromSeconds(5)));

            await completion.Complete();

            Assert.AreEqual(1, Flatten(pushedItems).Sum(i => i));
        }
        public void Initialize(EntityInfo entity, Func <IncomingMessageDetails, ReceiveContext, Task> callback, Func <Exception, Task> errorCallback, Func <ErrorContext, Task <ErrorHandleResult> > processingFailureCallback, int maximumConcurrency)
        {
            receiveMode = settings.Get <ReceiveMode>(WellKnownConfigurationKeys.Connectivity.MessageReceivers.ReceiveMode);

            incomingCallback               = callback;
            this.errorCallback             = errorCallback ?? EmptyErrorCallback;
            this.processingFailureCallback = processingFailureCallback;
            this.entity = entity;

            fullPath = entity.Path;
            if (entity.Type == EntityType.Subscription)
            {
                var topic = entity.RelationShips.First(r => r.Type == EntityRelationShipType.Subscription);
                fullPath = SubscriptionClient.FormatSubscriptionPath(topic.Target.Path, entity.Path);
            }

            var transportTransactionMode = settings.HasExplicitValue <TransportTransactionMode>() ? settings.Get <TransportTransactionMode>() : settings.SupportedTransactionMode();

            wrapInScope            = transportTransactionMode == TransportTransactionMode.SendsAtomicWithReceive;
            completionCanBeBatched = !wrapInScope;
            autoRenewTimeout       = settings.Get <TimeSpan>(WellKnownConfigurationKeys.Connectivity.MessageReceivers.AutoRenewTimeout);
            numberOfClients        = settings.Get <int>(WellKnownConfigurationKeys.Connectivity.NumberOfClientsPerEntity);
            var concurrency = maximumConcurrency / (double)numberOfClients;

            maxConcurrentCalls = concurrency > 1 ? (int)Math.Round(concurrency, MidpointRounding.AwayFromZero) : 1;
            if (Math.Abs(maxConcurrentCalls - concurrency) > 0)
            {
                logger.InfoFormat("The maximum concurrency on message receiver instance for '{0}' has been adjusted to '{1}', because the total maximum concurrency '{2}' wasn't divisable by the number of clients '{3}'", fullPath, maxConcurrentCalls, maximumConcurrency, numberOfClients);
            }

            internalReceivers = new IMessageReceiver[numberOfClients];
            onMessageOptions  = new OnMessageOptions[numberOfClients];
            completion        = new MultiProducerConcurrentCompletion <Guid>(1000, TimeSpan.FromSeconds(1), 6, numberOfClients);
        }
        public async Task Pushing_for_slots_before_start_works_when_pushInterval_reached()
        {
            var receivedItems = new ConcurrentQueue <List <int> >[4]
            {
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
                new ConcurrentQueue <List <int> >(),
            };

            var countDownEvent = new CountdownEvent(4);

            // choose insanely high batchSize
            var completion = new MultiProducerConcurrentCompletion <int>(batchSize: 10000, pushInterval: TimeSpan.FromMilliseconds(1), maxConcurrency: 4, numberOfSlots: 4);

            var numberOfItems = await PushConcurrentlyTwoThousandItemsInPackagesOfFiveHundredIntoFourSlots(completion);

            completion.Start(async(items, slot, state, token) =>
            {
                await Task.Yield();

                receivedItems[slot].Enqueue(new List <int>(items)); // take a copy
                if (!countDownEvent.IsSet)
                {
                    countDownEvent.Signal();
                }
            });

            await Task.Run(() => countDownEvent.Wait(TimeSpan.FromSeconds(5)));

            var sumOfAllElementsSeenSoFar = Flatten(receivedItems).Sum(i => i);

            await completion.Complete();

            Assert.AreEqual(TriangularNumber(numberOfItems), sumOfAllElementsSeenSoFar);
            Assert.AreEqual(TriangularNumber(numberOfItems), Flatten(receivedItems).Sum(i => i), "Dispatcher complete brought more items than expected");
        }
        static async Task <int> PushConcurrentlyTwoThousandItemsInPackagesOfFiveHundredIntoFourSlots(MultiProducerConcurrentCompletion <int> completion)
        {
            var t1 = Task.Run(() => Parallel.For(1, 500, i =>
            {
                completion.Push(slotNumber: 0, item: i);
            }));

            var t2 = Task.Run(() => Parallel.For(500, 1000, i =>
            {
                completion.Push(slotNumber: 1, item: i);
            }));

            var t3 = Task.Run(() => Parallel.For(1000, 1500, i =>
            {
                completion.Push(slotNumber: 2, item: i);
            }));

            await Task.WhenAll(t1, t2, t3);

            var numberOfItems = 2000;

            for (var i = 1500; i < numberOfItems + 1; i++)
            {
                completion.Push(slotNumber: 3, item: i);
            }
            return(numberOfItems);
        }