Exemplo n.º 1
0
        /// <summary>
        /// Invoke the receive side of the default
        /// pipeline with the passed message
        /// </summary>
        /// <param name="provider"></param>
        /// <param name="id"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private static async Task Invoke(
            IServiceProvider provider,
            string id,
            object message
            )
        {
            var context = new FakeTransactionContext();

            var msg = new Message(
                new Dictionary <string, string>()
            {
                { Headers.MessageId, id }
            },
                message
                );

            var serializer = provider.GetService <ISerializer>();
            var invoker    = provider.GetService <IPipelineInvoker>();

            AmbientTransactionContext.SetCurrent(context);

            var transportMessage = await serializer.Serialize(msg);

            var ctx = new IncomingStepContext(transportMessage, context);

            await invoker.Invoke(ctx);

            context.Dispose();
        }
Exemplo n.º 2
0
            void DoWork()
            {
                while (keepWorking)
                {
                    try
                    {
                        using (var tx = new TransactionScope())
                        {
                            var transactionContext = new AmbientTransactionContext();
                            var message            = queue.ReceiveMessage(transactionContext);
                            if (message == null)
                            {
                                Thread.Sleep(200);
                                continue;
                            }

                            var destinationForThisMessage = getNextDestination();

                            log.Debug("Received message {0} will be forwarded to {1}", message.Id, destinationForThisMessage);

                            queue.Send(destinationForThisMessage, message.ToForwardableMessage(), transactionContext);

                            tx.Complete();
                        }
                    }
                    catch (Exception exception)
                    {
                        log.Error("An error occurred while trying to process a message: {0}", exception);
                    }
                }
            }
Exemplo n.º 3
0
        public void CanSendMessagesDirectlyToExchange(int mode)
        {
            // arrange
            var routeUsingExchanges = mode > 1;
            var exchangeAsAddress   = mode > 2;
            var sender = GetQueue("test.sender", removeExiting: true, oneExchangePerType: routeUsingExchanges,
                                  inputExchange: exchangeAsAddress ? "ex-test.sender" : null);
            var recipient = GetQueue("test.recipient", removeExiting: true, oneExchangePerType: true, inputExchange: "ex-test.recipient");

            // act
            using (var tx = new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();
                var msg = new TransportMessageToSend {
                    Body = Encoding.GetBytes("this is a message!")
                };

                sender.Send("@ex-test.recipient", msg, ctx);
                sender.Send("@ex-test.recipient", msg, ctx);
                sender.Send("@ex-test.recipient", msg, ctx);

                tx.Complete();
            }

            // assert
            var receivedTransportMessages = GetAllMessages(recipient);

            receivedTransportMessages.Count.ShouldBe(3);
        }
Exemplo n.º 4
0
        public void CanSendMessagesInTransaction(bool commitTransactionAndExpectMessagesToBeThere)
        {
            // arrange
            var sender    = GetQueue("test.tx.sender");
            var recipient = GetQueue("test.tx.recipient");

            // act
            using (var tx = new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();
                var msg = new TransportMessageToSend {
                    Body = Encoding.GetBytes("this is a message!")
                };

                sender.Send(recipient.InputQueue, msg, ctx);
                sender.Send(recipient.InputQueue, msg, ctx);
                sender.Send(recipient.InputQueue, msg, ctx);

                if (commitTransactionAndExpectMessagesToBeThere)
                {
                    tx.Complete();
                }
            }

            // assert
            var receivedTransportMessages = GetAllMessages(recipient);

            receivedTransportMessages.Count.ShouldBe(commitTransactionAndExpectMessagesToBeThere ? 3 : 0);
        }
Exemplo n.º 5
0
        async Task ProcessMessage(TransactionContext context, TransportMessage transportMessage)
        {
            try
            {
                AmbientTransactionContext.SetCurrent(context);

                var stepContext = new IncomingStepContext(transportMessage, context);
                await _pipelineInvoker.Invoke(stepContext);

                try
                {
                    await context.Complete();
                }
                catch (Exception exception)
                {
                    _log.Error(exception, "An error occurred when attempting to complete the transaction context");
                }
            }
            catch (OperationCanceledException exception)
            {
                context.Abort();

                _log.Error(exception, "Worker was aborted while handling message {messageLabel}", transportMessage.GetMessageLabel());
            }
            catch (Exception exception)
            {
                context.Abort();

                _log.Error(exception, "Unhandled exception while handling message {messageLabel}", transportMessage.GetMessageLabel());
            }
            finally
            {
                AmbientTransactionContext.SetCurrent(null);
            }
        }
Exemplo n.º 6
0
        public void MessageIsSentWhenAmbientTransactionIsCommitted()
        {
            using (var tx = new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();
                senderQueue.Send(DestinationQueueName,
                                 serializer.Serialize(new Message
                {
                    Messages = new object[]
                    {
                        "W00t!"
                    },
                }),
                                 ctx);

                tx.Complete();
            }

            var msmqMessage = Receive();

            Assert.IsNotNull(msmqMessage, "No message was received within timeout!");
            var transportMessage = (ReceivedTransportMessage)msmqMessage.Body;
            var message          = serializer.Deserialize(transportMessage);

            message.Messages[0].ShouldBe("W00t!");
        }
Exemplo n.º 7
0
        void DoWork(IReceiveMessages messageQueue)
        {
            try
            {
                using (var tx = new TransactionScope())
                {
                    var ctx = new AmbientTransactionContext();
                    var receivedTransportMessage = messageQueue.ReceiveMessage(ctx);

                    if (receivedTransportMessage == null)
                    {
                        return;
                    }

                    try
                    {
                        SendMessage(receivedTransportMessage);

                        tx.Complete();
                    }
                    catch (Exception e)
                    {
                        log.Error(e, "Could not send message {0} to destination URI {1} - waiting 1 sec before retrying",
                                  receivedTransportMessage.Id, destinationUri);

                        Thread.Sleep(TimeSpan.FromSeconds(1));
                    }
                }
            }
            catch (Exception e)
            {
                log.Error("Unhandled exception during receive operation", e);
            }
        }
Exemplo n.º 8
0
        public void MessageIsNotSentWhenAmbientTransactionIsNotCommitted()
        {
            using (new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();
                senderQueue.Send(DestinationQueueName,
                                 serializer.Serialize(new Message
                {
                    Messages = new object[]
                    {
                        "W00t! should not be delivered!"
                    }
                }),
                                 ctx);

                //< we exit the scope without completing it!
            }

            var transportMessage = Receive();

            if (transportMessage != null)
            {
                Assert.Fail("No messages should have been received! ARGGH: {0}", transportMessage.Body);
            }
        }
Exemplo n.º 9
0
        async Task TryProcessNextMessage()
        {
            var parallelOperation = _parallelOperationsManager.TryBegin();

            if (!parallelOperation.CanContinue())
            {
                await _backoffStrategy.WaitAsync(_cancellationToken);

                return;
            }

            try
            {
                using (parallelOperation)
                    using (var context = new TransactionContextWithOwningBus(_owningBus))
                    {
                        var transportMessage = await ReceiveTransportMessage(_cancellationToken, context);

                        if (transportMessage == null)
                        {
                            context.Dispose();

                            // get out quickly if we're shutting down
                            if (_cancellationToken.IsCancellationRequested || _busDisposalCancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            // no need for another thread to rush in and discover that there is no message
                            //parallelOperation.Dispose();

                            await _backoffStrategy.WaitNoMessageAsync(_cancellationToken);

                            return;
                        }

                        _backoffStrategy.Reset();

                        try
                        {
                            AmbientTransactionContext.SetCurrent(context);
                            await ProcessMessage(context, transportMessage);
                        }
                        finally
                        {
                            AmbientTransactionContext.SetCurrent(null);
                        }
                    }
            }
            catch (OperationCanceledException) when(_cancellationToken.IsCancellationRequested || _busDisposalCancellationToken.IsCancellationRequested)
            {
                // we're shutting down
            }
            catch (Exception exception)
            {
                _log.Error(exception, "Unhandled exception in worker {workerName}", Name);
            }
        }
Exemplo n.º 10
0
        public void CanReceiveAndDoOneSingleSendAtomically(bool commitTransactionAndExpectMessagesToBeThere)
        {
            sender.Send(receiver.InputQueueAddress, MessageWith("hello"), new NoTransaction());

            var destination1 = factory.CreateReceiver("destination1");

            Thread.Sleep(300.Milliseconds());

            // pretend that this is a message handler tx scope...
            using (var tx = new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();

                // arrange
                var receivedTransportMessage = receiver.ReceiveMessage(ctx);
                Assert.That(receivedTransportMessage, Is.Not.Null);

                // act
                sender.Send(destination1.InputQueueAddress, MessageWith("hello mr. 1"), ctx);

                if (commitTransactionAndExpectMessagesToBeThere)
                {
                    tx.Complete();
                }
            }

            Thread.Sleep(300.Milliseconds());

            // assert
            var msg1 = destination1.ReceiveMessage(new NoTransaction());

            if (commitTransactionAndExpectMessagesToBeThere)
            {
                Assert.That(msg1, Is.Not.Null);
                Assert.That(Encoding.GetString(msg1.Body), Is.EqualTo("hello mr. 1"));

                using (new TransactionScope())
                {
                    var ctx = new AmbientTransactionContext();
                    var receivedTransportMessage = receiver.ReceiveMessage(ctx);
                    Assert.That(receivedTransportMessage, Is.Null);
                }
            }
            else
            {
                Assert.That(msg1, Is.Null);

                using (new TransactionScope())
                {
                    var ctx = new AmbientTransactionContext();
                    var receivedTransportMessage = receiver.ReceiveMessage(ctx);
                    Assert.That(receivedTransportMessage, Is.Not.Null);
                    Assert.That(Encoding.GetString(receivedTransportMessage.Body), Is.EqualTo("hello"));
                }
            }
        }
Exemplo n.º 11
0
        public void MultipleSendOperationsToMultipleQueuesAreEnlistedInTheSameTransaction(bool commitTransactionAndExpectMessagesToBeThere)
        {
            // arrange
            const string queueName1 = "test.tx.queue1";
            const string queueName2 = "test.tx.queue2";

            var recipient1 = NewRawMsmqQueue(queueName1);
            var recipient2 = NewRawMsmqQueue(queueName2);

            disposables.Add(recipient1);
            disposables.Add(recipient2);

            var encoding = Encoding.UTF8;

            using (var tx = new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();

                // act
                senderQueue.Send(queueName1, new TransportMessageToSend {
                    Body = encoding.GetBytes("yo dawg 1!")
                }, ctx);
                senderQueue.Send(queueName1, new TransportMessageToSend {
                    Body = encoding.GetBytes("yo dawg 2!")
                }, ctx);
                senderQueue.Send(queueName2, new TransportMessageToSend {
                    Body = encoding.GetBytes("yo dawg 3!")
                }, ctx);
                senderQueue.Send(queueName2, new TransportMessageToSend {
                    Body = encoding.GetBytes("yo dawg 4!")
                }, ctx);

                if (commitTransactionAndExpectMessagesToBeThere)
                {
                    tx.Complete();
                }
            }

            // assert
            var allMessages = GetAllMessages(recipient1).Concat(GetAllMessages(recipient2)).ToList();

            if (commitTransactionAndExpectMessagesToBeThere)
            {
                allMessages.Count.ShouldBe(4);

                var receivedMessages = allMessages.Select(m => encoding.GetString(m.Body)).ToList();
                receivedMessages.ShouldContain("yo dawg 1!");
                receivedMessages.ShouldContain("yo dawg 2!");
                receivedMessages.ShouldContain("yo dawg 3!");
                receivedMessages.ShouldContain("yo dawg 4!");
            }
            else
            {
                allMessages.Count.ShouldBe(0);
            }
        }
Exemplo n.º 12
0
        public void CleanupIsPerformedEvenIfTransactionIsNotCommitted()
        {
            int cleanupCalled = 0;
            var tx            = new TransactionScope();
            var ctx           = new AmbientTransactionContext();

            ctx.Cleanup += () => { cleanupCalled++; };

            tx.Dispose();
            cleanupCalled.ShouldBe(1);
        }
Exemplo n.º 13
0
        public void CleanupIsPerformedOnDispose()
        {
            bool cleanupCalled = false;
            var  tx            = new TransactionScope();
            var  ctx           = new AmbientTransactionContext();

            ctx.Cleanup += () => { cleanupCalled = true; };
            tx.Dispose();

            cleanupCalled.ShouldBe(true);
        }
Exemplo n.º 14
0
        public void CleanupIsPerformedOnce()
        {
            int cleanupCalled = 0;
            var tx            = new TransactionScope();
            var ctx           = new AmbientTransactionContext();

            ctx.Cleanup += () => { cleanupCalled++; };

            tx.Complete();
            cleanupCalled.ShouldBe(0);
            tx.Dispose();
            cleanupCalled.ShouldBe(1);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Extension method on <see cref="IBus"/> that allows for asynchronously publishing a request and dispatching
        /// the received reply to the continuation.
        /// </summary>
        /// <typeparam name="TReply">Specifies the expected type of the reply. Can be any type compatible with the actually received reply</typeparam>
        /// <param name="bus">The bus instance to use to publish the request</param>
        /// <param name="request">The request message</param>
        /// <param name="optionalHeaders">Headers to be included in the request message</param>
        /// <param name="timeout">Optionally specifies the max time to wait for a reply. If this time is exceeded, a <see cref="TimeoutException"/> is thrown</param>
        /// <param name="externalCancellationToken">An external cancellation token from some outer context that cancels waiting for a reply</param>
        /// <returns></returns>
        public static async Task <TReply> PublishRequest <TReply>(this IBus bus, object request, IDictionary <string, string> optionalHeaders = null, TimeSpan?timeout = null, CancellationToken externalCancellationToken = default)
        {
            var currentTransactionContext = AmbientTransactionContext.Current;

            try
            {
                AmbientTransactionContext.SetCurrent(null);
                return(await InnerProcessRequest <TReply>((eventMessage, headers) => bus.Publish(eventMessage, headers), request, optionalHeaders, timeout, externalCancellationToken));
            }
            finally
            {
                AmbientTransactionContext.SetCurrent(currentTransactionContext);
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Extension method on <see cref="IBus"/> that allows for asynchronously sending a request and dispatching
        /// the received reply to the continuation.
        /// </summary>
        /// <typeparam name="TReply">Specifies the expected type of the reply. Can be any type compatible with the actually received reply</typeparam>
        /// <param name="bus">The bus instance to use to send the request</param>
        /// <param name="request">The request message</param>
        /// <param name="optionalHeaders">Headers to be included in the request message</param>
        /// <param name="timeout">Optionally specifies the max time to wait for a reply. If this time is exceeded, a <see cref="TimeoutException"/> is thrown</param>
        /// <returns></returns>
        public static async Task <TReply> SendRequest <TReply>(this IBus bus, object request, Dictionary <string, string> optionalHeaders = null, TimeSpan?timeout = null)
        {
            var currentTransactionContext = AmbientTransactionContext.Current;

            try
            {
                AmbientTransactionContext.SetCurrent(null);
                return(await InnerSendRequest <TReply>(bus, request, optionalHeaders, timeout));
            }
            finally
            {
                AmbientTransactionContext.SetCurrent(currentTransactionContext);
            }
        }
Exemplo n.º 17
0
        public void DoesNotBlockOnCompletingTransactionContext()
        {
            var bus        = _activator.Bus.Advanced.SyncBus;
            var gotMessage = new ManualResetEvent(false);

            _activator.Handle <string>(async str => gotMessage.Set());

            using (var aspNet = new AspNetSimulatorSynchronizationContext())
            {
                aspNet.Post(s =>
                {
                    using (var context = new DefaultSyncTransactionContextScope())
                    {
                        var transactionContext = AmbientTransactionContext.Current;

                        try
                        {
                            // enlist some other async thing
                            transactionContext.OnCommitted(async() =>
                            {
                                Console.WriteLine("waiting....");
                                await Task.Delay(100);
                                Console.WriteLine("waiting....");
                                await Task.Delay(100);
                                Console.WriteLine("waiting....");
                                await Task.Delay(100);
                            });

                            // enlist an operation in the context
                            bus.SendLocal("HEJ MED DIG MIN VEN");

                            context.Complete();
                        }
                        finally
                        {
                            AmbientTransactionContext.SetCurrent(null);
                        }
                    }
                }, null);

                gotMessage.WaitOrDie(TimeSpan.FromSeconds(3));
            }
        }
        public void DoesNotLeakConnectionsForEachMessage()
        {
            // Low timeout + pool size will quickly show the issue.
            var cs        = ConnectionString + ";Connect Timeout = 5;Max Pool Size=10";
            var transport = new SqlServerMessageQueue(cs, TableName, InputQueueName);

            transport.EnsureTableIsCreated();
            transport.PurgeInputQueue();

            for (int i = 0; i < 100; i++)
            {
                using (var tx = new TransactionScope())
                {
                    var ctx = new AmbientTransactionContext();
                    transport.Send(InputQueueName, new TransportMessageToSend()
                    {
                        Body = new byte[0], Headers = new Dictionary <string, object>(), Label = "test"
                    }, ctx);
                    tx.Complete();
                }
            }
        }
Exemplo n.º 19
0
        async Task ProcessMessage(TransactionContext context, TransportMessage transportMessage)
        {
            try
            {
                context.Items["OwningBus"] = _owningBus;

                AmbientTransactionContext.SetCurrent(context);

                var incomingSteps = _pipeline.ReceivePipeline();
                var stepContext   = new IncomingStepContext(transportMessage, context);
                await _pipelineInvoker.Invoke(stepContext, incomingSteps);

                try
                {
                    await context.Complete();
                }
                catch (Exception exception)
                {
                    _log.Error(exception, "An error occurred when attempting to complete the transaction context");
                }
            }
            catch (ThreadAbortException exception)
            {
                context.Abort();

                _log.Error(exception, $"Worker was killed while handling message {transportMessage.GetMessageLabel()}");
            }
            catch (Exception exception)
            {
                context.Abort();

                _log.Error(exception, $"Unhandled exception while handling message {transportMessage.GetMessageLabel()}");
            }
            finally
            {
                AmbientTransactionContext.SetCurrent(null);
            }
        }
Exemplo n.º 20
0
        public void CanSendMessagesDirectlyToExchangeWithRoutingKey(int mode)
        {
            // arrange
            var routeUsingExchanges = mode > 1;
            var exchangeAsAddress   = mode > 2;
            var sender = GetQueue("test.sender", removeExiting: true, oneExchangePerType: routeUsingExchanges,
                                  inputExchange: exchangeAsAddress ? "ex-test.sender" : null);
            var recipient = GetQueue("test.recipient", removeExiting: true);

            using (var cm = new ConnectionManager(ConnectionString, "test.sender"))
                using (var conn = cm.GetConnection())
                    using (var model = conn.CreateModel())
                    {
                        model.QueueBind("test.recipient", "ex-test.recipient", "sample");
                    }

            // act
            using (var tx = new TransactionScope())
            {
                var ctx = new AmbientTransactionContext();
                var msg = new TransportMessageToSend {
                    Body = Encoding.GetBytes("this is a message!")
                };

                sender.Send("*****@*****.**", msg, ctx);
                sender.Send("*****@*****.**", msg, ctx);
                sender.Send("*****@*****.**", msg, ctx);

                tx.Complete();
            }

            // assert
            var receivedTransportMessages = GetAllMessages(recipient);

            receivedTransportMessages.Count.ShouldBe(3);
        }
Exemplo n.º 21
0
    protected override void TearDown()
    {
        AmbientTransactionContext.SetCurrent(null);

        _activator.Dispose();
    }
Exemplo n.º 22
0
        static void Run(Parameters parameters)
        {
            if (string.IsNullOrWhiteSpace(parameters.ErrorQueueName))
            {
                throw new NiceException("Please specify the name of an error queue");
            }
            if (string.IsNullOrWhiteSpace(parameters.Host))
            {
                throw new NiceException("Please specify the hostname");
            }

            using (var rabbitMqMessageQueue = new RabbitMqMessageQueue(parameters.Host, parameters.ErrorQueueName))
            {
                using (var tx = new TransactionScope())
                {
                    var transactionContext = new AmbientTransactionContext();

                    var allTheMessages = GetAllTheMessages(rabbitMqMessageQueue, transactionContext);

                    foreach (var message in allTheMessages)
                    {
                        var transportMessageToSend = message.ToForwardableMessage();
                        try
                        {
                            if (!transportMessageToSend.Headers.ContainsKey(Headers.SourceQueue))
                            {
                                throw new NiceException("Message {0} does not have a source queue header - it will be moved back to the input queue",
                                                        message.Id);
                            }

                            var sourceQueue = (string)transportMessageToSend.Headers[Headers.SourceQueue];

                            if (parameters.AutoMoveAllMessages.GetValueOrDefault())
                            {
                                rabbitMqMessageQueue.Send(sourceQueue, transportMessageToSend, transactionContext);

                                Print("Moved {0} to {1}", message.Id, sourceQueue);
                            }
                            else
                            {
                                var answer = PromptChar(new[] { 'y', 'n' }, "Would you like to move {0} to {1}? (y/n)",
                                                        message.Id, sourceQueue);

                                if (answer == 'y')
                                {
                                    rabbitMqMessageQueue.Send(sourceQueue, transportMessageToSend, transactionContext);

                                    Print("Moved {0} to {1}", message.Id, sourceQueue);
                                }
                                else
                                {
                                    rabbitMqMessageQueue.Send(rabbitMqMessageQueue.InputQueueAddress,
                                                              transportMessageToSend,
                                                              transactionContext);

                                    Print("Moved {0} to {1}", message.Id, rabbitMqMessageQueue.InputQueueAddress);
                                }
                            }
                        }
                        catch (NiceException e)
                        {
                            Print(e.Message);

                            rabbitMqMessageQueue.Send(rabbitMqMessageQueue.InputQueueAddress,
                                                      transportMessageToSend,
                                                      transactionContext);
                        }
                    }

                    if (parameters.DryRun.GetValueOrDefault())
                    {
                        Print("Aborting queue transaction");
                        return;
                    }

                    if (!parameters.Interactive)
                    {
                        tx.Complete();
                        return;
                    }

                    var commitAnswer = PromptChar(new[] { 'y', 'n' }, "Would you like to commit the queue transaction?");

                    if (commitAnswer == 'y')
                    {
                        Print("Committing queue transaction");

                        tx.Complete();
                        return;
                    }

                    Print("Queue transaction aborted");
                }
            }
        }
Exemplo n.º 23
0
        public void CanSendAndReceiveMessages(int count, int consumers)
        {
            const string senderInputQueue   = "test.rabbit.sender";
            const string receiverInputQueue = "test.rabbit.receiver";

            var sender   = GetQueue(senderInputQueue);
            var receiver = GetQueue(receiverInputQueue);

            var totalMessageCount = count * consumers;

            var stopwatch = Stopwatch.StartNew();

            Console.WriteLine("Sending {0} messages", totalMessageCount);
            Enumerable.Range(0, totalMessageCount).ToList()
            .ForEach(
                i => sender.Send(receiverInputQueue,
                                 new TransportMessageToSend {
                Body = Encoding.UTF7.GetBytes("w00t! message " + i)
            },
                                 new NoTransaction()));

            var totalSeconds = stopwatch.Elapsed.TotalSeconds;

            Console.WriteLine("Sending {0} messages took {1:0.0} s - that's {2:0} msg/s", totalMessageCount, totalSeconds, totalMessageCount / totalSeconds);

            Thread.Sleep(1.Seconds());

            Console.WriteLine("Receiving {0} messages", totalMessageCount);
            long receivedMessageCount = 0;

            stopwatch = Stopwatch.StartNew();

            var threads = Enumerable.Range(0, consumers)
                          .Select(i => new Thread(_ =>
            {
                var gotNoMessageCount = 0;
                do
                {
                    using (var scope = new TransactionScope())
                    {
                        var ctx = new AmbientTransactionContext();
                        var receivedTransportMessage = receiver.ReceiveMessage(ctx);
                        if (receivedTransportMessage == null)
                        {
                            gotNoMessageCount++;
                            continue;
                        }
                        Encoding.UTF7.GetString(receivedTransportMessage.Body).ShouldStartWith("w00t! message ");
                        Interlocked.Increment(ref receivedMessageCount);

                        scope.Complete();
                    }
                } while (gotNoMessageCount < 3);
            }))
                          .ToList();

            threads.ForEach(t => t.Start());
            threads.ForEach(t => t.Join());

            totalSeconds = stopwatch.Elapsed.TotalSeconds;

            Console.WriteLine("Receiving {0} messages spread across {1} consumers took {2:0.0} s - that's {3:0} msg/s",
                              totalMessageCount, consumers, totalSeconds, totalMessageCount / totalSeconds);

            receivedMessageCount.ShouldBe(totalMessageCount);
        }