コード例 #1
0
        public async Task Can_deliver_single_batch_of_messages_fully()
        {
            const int expected = 1000;
            var       actual   = 0;

            // we have ten background workers trying to consume messages from an internal buffer
            var producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 10
            };

            producer.Attach(x => { Interlocked.Increment(ref actual); });
            await producer.Start();

            // while those workers are waiting for new messages, we feed the production
            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }

            // then, we stop the service in non-immediate mode;
            // this should result in all the messages being sent since
            // we clear the buffer before shutting down delivery
            await producer.Stop(immediate : false);

            while (actual != expected)
            {
                await Task.Delay(10);
            }
        }
コード例 #2
0
        public async Task Can_track_delivery_rate()
        {
            const int expected = 100000;
            var       actual   = 0;

            // we have ten background workers trying to consume messages from an internal buffer
            var producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 10
            };

            producer.Attach(x => { Interlocked.Increment(ref actual); });

            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }

            await producer.Start();

            await producer.Stop();

            _console.WriteLine($"Delivered: {producer.Sent}");
            _console.WriteLine($"Uptime: {producer.Uptime}");
            _console.WriteLine($"Delivery rate: {producer.Rate} msgs / second");
        }
コード例 #3
0
        public async Task Can_manage_errors_with_custom_retry_policy()
        {
            const int expected = 1000;
            var       actual   = 0;
            var       producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 10
            };

            producer.AttachError(x =>
            {
                /* compensating for not using an intermediary like Hub, which handles exceptions */
            });
            producer.AttachUndeliverable(new Action <BaseEvent>(x => Interlocked.Increment(ref actual)));
            producer.Attach(new ThrowingHandler());

            producer.RetryPolicy = new RetryPolicy();
            producer.RetryPolicy.After(1, RetryDecision.Undeliverable);

            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }
            await producer.Start();

            await producer.Stop();
        }
コード例 #4
0
        public async Task Can_cap_delivery_rate()
        {
            const int expected = 3000;
            var       actual   = 0;
            var       producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 10
            };

            producer.Attach(x => { Interlocked.Increment(ref actual); });

            producer.RateLimitPolicy = new RateLimitPolicy
            {
                Enabled     = true,
                Occurrences = 1000,
                TimeUnit    = TimeSpan.FromSeconds(1)
            };

            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }

            await producer.Start();

            await producer.Stop();

            Assert.Equal(expected, producer.Sent);
            Assert.True(Math.Abs(producer.Rate - producer.RateLimitPolicy.Occurrences) < expected * 1.25f);

            _console.WriteLine($"Delivered: {producer.Sent}");
            _console.WriteLine($"Uptime: {producer.Uptime}");
            _console.WriteLine($"Delivery rate: {producer.Rate} msgs / second");
        }
コード例 #5
0
        public async Task Can_requeue_with_exponential_backoff()
        {
            const int expected = 1;
            const int maxTries = 3;

            var actual = 0;

            var producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 10
            };

            producer.AttachError(x =>
            {
                /* compensating for not using an intermediary like Hub, which handles exceptions */
            });
            producer.Attach(new ThrowingHandler());
            producer.AttachUndeliverable(x =>
            {
                _console.WriteLine("abandoned message after " + x.Attempts + " attempts");
                Assert.Equal(maxTries, x.Attempts);
                Interlocked.Increment(ref actual);
            });

            producer.RetryPolicy = new RetryPolicy();
            producer.RetryPolicy.After(maxTries, RetryDecision.Undeliverable);

            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }
            await producer.Start();

            while (producer.Undeliverable < expected)
            {
                await Task.Delay(10);
            }
            await producer.Stop();

            Assert.Equal(expected, actual);
        }
コード例 #6
0
        public ScheduledProducer(ScheduledProducerSettings settings = null)
        {
            _settings = settings ?? new ScheduledProducerSettings();

            _schedulers = new ConcurrentDictionary <int, TaskScheduler>();
            _factories  = new ConcurrentDictionary <TaskScheduler, TaskFactory>();
            _pending    = new ConcurrentDictionary <Handler, HandlerMethods>();
            _cancel     = new CancellationTokenSource();
            _threads    = _settings.Concurrency;

            // polling thread
            Background = new BackgroundThreadProducer <IEnumerable <ScheduledTask> >();
            Background.Attach(WithPendingTasks);
            Background.AttachBacklog(WithOverflowTasks);
            Background.AttachUndeliverable(WithFailedTasks);

            // maintenance thread
            Maintenance = new BackgroundThreadProducer <IEnumerable <ScheduledTask> >();
            Maintenance.Attach(WithHangingTasks);
            Maintenance.AttachBacklog(WithHangingTasks);
            Maintenance.AttachUndeliverable(WithHangingTasks);
        }
コード例 #7
0
        public async Task Can_deliver_single_batch_of_messages_with_overrun_in_backlog()
        {
            const int expected   = 1000;
            var       actual     = 0;
            var       backlogged = 0;

            // we have ten background workers trying to consume messages from an internal buffer
            var producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 10
            };

            producer.AttachBacklog(new Action <BaseEvent>(x => Interlocked.Increment(ref backlogged)));
            producer.Attach(x => { Interlocked.Increment(ref actual); });

            producer.RetryPolicy = new RetryPolicy();
            producer.RetryPolicy.After(1, RetryDecision.Backlog);

            await producer.Start();

            // while those workers are waiting for new messages, we feed the production
            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }

            // then, we stop the service in immediate mode;
            // this should result in all undelivered messages redirecting to the backlog consumer
            // before shutting down delivery
            await producer.Stop(immediate : true);

            while (actual + backlogged != expected)
            {
                await Task.Delay(10);
            }

            _console.WriteLine($"{actual} sent");
            _console.WriteLine($"{backlogged} backlogged");
        }
コード例 #8
0
        public async Task Can_perform_ack_nack_pattern()
        {
            var random = new Random();

            const int expected = 100;
            var       actual   = 0;     // counting finalizations

            var retryPolicy = new RetryPolicy();

            retryPolicy.Default(RetryDecision.Backlog);             // use external requeuing
            retryPolicy.After(3, RetryDecision.Undeliverable);

            //
            // ACK/NACK:
            // =========
            // 1. The main thread pushes messages, while two consumer producers pull messages.
            // 2. If the message fails (50% probability), it is returned to the push thread.
            //    The number of attempts are retained when the message is relayed. This way
            //    the retry policy is enforced across all consumers, provided each consumer
            //    implements the same policy.

            var producer = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 1
            };
            var consumer1 = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 1
            };
            var consumer2 = new BackgroundThreadProducer <BaseEvent> {
                MaxDegreeOfParallelism = 1
            };

            //
            // Consumer 1:
            consumer1.AttachUndeliverable(x =>
            {
                _console.WriteLine("[consumer2] message " + x.Message.Id + " abandoned after " + x.Attempts + " attempt(s)");
                Interlocked.Increment(ref actual);                 // undelivered
            });
            consumer1.AttachBacklog(async x =>
            {
                _console.WriteLine("[consumer1] message " + x.Message.Id + " requeued");
                await SendToRandomConsumer(x);                 // passes context data to other consumers
            });
            consumer1.Attach(new ActionConsumer <BaseEvent>(x =>
            {
                if (NextBool(random))
                {
                    throw new Exception();
                }
                Interlocked.Increment(ref actual);                 // sent
            }));
            consumer1.RetryPolicy = retryPolicy;
            await consumer1.Start();

            //
            // Consumer 2:
            consumer2.AttachUndeliverable(x =>
            {
                _console.WriteLine("[consumer2] message " + x.Message.Id + " abandoned after " + x.Attempts + " attempt(s)");
                Interlocked.Increment(ref actual);                 // undelivered
            });
            consumer2.AttachBacklog(async x =>
            {
                _console.WriteLine("[consumer2] message " + x.Message.Id + " requeued");
                await SendToRandomConsumer(x);                 // passes context data to other consumers
            });
            consumer2.Attach(new ActionConsumer <BaseEvent>(x =>
            {
                if (NextBool(random))
                {
                    throw new Exception();
                }
                Interlocked.Increment(ref actual);                 // sent
            }));
            consumer2.RetryPolicy = retryPolicy;
            await consumer2.Start();

            async Task SendToRandomConsumer(QueuedMessage <BaseEvent> x)
            {
                if (NextBool(random))
                {
                    _console.WriteLine($"message {x.Message.Id} sent to consumer1 ({x.Attempts} attempt(s))");
                    await consumer1.Produce(x);
                }
                else
                {
                    _console.WriteLine($"message {x.Message.Id} sent to consumer2 ({x.Attempts} attempt(s))");
                    await consumer2.Produce(x);
                }
            }

            //
            // Producer:
            for (var i = 0; i < expected; i++)
            {
                await producer.Produce(new BaseEvent { Id = i });
            }
            producer.Attach(async x => { await SendToRandomConsumer(x); });
            await producer.Start();             // start with a full buffer

            while (actual != expected)          // wait for all finalizations
            {
                await Task.Delay(10);
            }
        }