Exemple #1
0
        public async Task ScheduleCommandForRetryIfEligibleAsyncSchedulesInTheFuture()
        {
            var initialPublishCount = 2;
            var publishTimes        = new List <Instant>();
            var mockPublisher       = new Mock <ICommandPublisher <SubmitOrderForProduction> >();
            var fakeClock           = new FakeClock(Instant.FromDateTimeUtc(new DateTime(2011, 11, 14, 03, 56, 22, DateTimeKind.Utc)));
            var functionBase        = new TestWebJobFunction(null);
            var thresholds          = new CommandRetryThresholds {
                CommandRetryMaxCount = 4, CommandRetryExponentialSeconds = 5, CommandRetryJitterSeconds = 1
            };
            var command = new SubmitOrderForProduction {
                PreviousAttemptsToHandleCount = initialPublishCount
            };
            var result = await functionBase.ScheduleCommandForRetryIfEligibleAsync(command, thresholds, new Random(), fakeClock, mockPublisher.Object);

            mockPublisher
            .Setup(publisher => publisher.PublishAsync(It.Is <SubmitOrderForProduction>(value => value == command), It.Is <Instant?>(value => value.HasValue)))
            .Returns(Task.CompletedTask)
            .Callback <SubmitOrderForProduction, Instant?>((publishedCommand, publishTime) => publishTimes.Add(publishTime.Value));



            result.Should().BeTrue("because the command should be recognized as schedulable");
            publishTimes.Count(time => time > fakeClock.GetCurrentInstant()).Should().Be(publishTimes.Count, "because all of the scheduled publish times should have been in the future");
        }
 public TestOrderSubmitterFunctions(JsonSerializer jsonSerializer,
                                    CommandRetryThresholds retryThresholds,
                                    IClock clock,
                                    IOrderSubmitter orderSubmitter,
                                    ICommandPublisher <SubmitOrderForProduction> submitOrderForProductionPublisher,
                                    ICommandPublisher <NotifyOfFatalFailure> notifyOfFatalFailurePublisher,
                                    IEventPublisher <EventBase> eventPublisher,
                                    ILogger logger,
                                    IDisposable lifetimeScope) : base(jsonSerializer, retryThresholds, clock, orderSubmitter, submitOrderForProductionPublisher, notifyOfFatalFailurePublisher, eventPublisher, logger, lifetimeScope)
 {
 }
Exemple #3
0
        public async Task ScheduleCommandForRetryIfEligibleAsyncDoesNotScheduleIfRetryCountIsExceeded()
        {
            var functionBase = new TestWebJobFunction(null);
            var thresholds   = new CommandRetryThresholds {
                CommandRetryMaxCount = 2
            };
            var command = new SubmitOrderForProduction {
                PreviousAttemptsToHandleCount = 3
            };
            var result = await functionBase.ScheduleCommandForRetryIfEligibleAsync(command, thresholds, new Random(), Mock.Of <IClock>(), Mock.Of <ICommandPublisher <SubmitOrderForProduction> >());

            result.Should().BeFalse("because the command had no remaining retries");
        }
Exemple #4
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="NotifierFunctions"/> class.
        /// </summary>
        ///
        /// <param name="jsonSerializer">The JSON serializer to use for parsing messages from the queue.</param>
        /// <param name="retryThresholds">The thresholds for use when retrying commands on a backoff policy.</param>
        /// <param name="clock">The clock to use for time-realated operations.</param>
        /// <param name="notifier">The notifier to use for sending notifications.</param>
        /// <param name="notifyOfFatalFailurePublisher">The publisher to use for sending of the <see cref="NotifyOfFatalFailure" /> command.</param>
        /// <param name="eventPublisher">The publisher to use for the sending of events.</param>
        /// <param name="logger">The logger to be used for emitting telemetry from the controller.</param>
        /// <param name="lifetimeScope">The lifetime scope associated with the class; this will be disposed when the class is by the base class.</param>
        ///
        public NotifierFunctions(JsonSerializer jsonSerializer,
                                 CommandRetryThresholds retryThresholds,
                                 IClock clock,
                                 INotifier notifier,
                                 ICommandPublisher <NotifyOfFatalFailure> notifyOfFatalFailurePublisher,
                                 IEventPublisher <EventBase> eventPublisher,
                                 ILogger logger,
                                 IDisposable lifetimeScope) : base(lifetimeScope)
        {
            this.jsonSerializer  = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer));
            this.retryThresholds = retryThresholds ?? throw new ArgumentNullException(nameof(retryThresholds));
            this.clock           = clock ?? throw new ArgumentNullException(nameof(clock));
            this.notifier        = notifier ?? throw new ArgumentNullException(nameof(notifier));
            this.notifyOfFatalFailurePublisher = notifyOfFatalFailurePublisher ?? throw new ArgumentNullException(nameof(notifyOfFatalFailurePublisher));
            this.eventPublisher = eventPublisher ?? throw new ArgumentNullException(nameof(eventPublisher));
            this.Log            = logger ?? throw new ArgumentNullException(nameof(logger));

            this.rng = new Random();
        }
Exemple #5
0
        public async Task ScheduleCommandForRetryIfEligibleAsyncSchedulesTheCommand()
        {
            var initialPublishCount = 2;
            var mockPublisher       = new Mock <ICommandPublisher <SubmitOrderForProduction> >();
            var fakeClock           = new FakeClock(Instant.FromDateTimeUtc(new DateTime(2011, 11, 14, 03, 56, 22, DateTimeKind.Utc)));
            var functionBase        = new TestWebJobFunction(null);
            var thresholds          = new CommandRetryThresholds {
                CommandRetryMaxCount = 4, CommandRetryExponentialSeconds = 1, CommandRetryJitterSeconds = 1
            };
            var command = new SubmitOrderForProduction {
                PreviousAttemptsToHandleCount = initialPublishCount
            };
            var result = await functionBase.ScheduleCommandForRetryIfEligibleAsync(command, thresholds, new Random(), fakeClock, mockPublisher.Object);

            result.Should().BeTrue("because the command should be recognized as schedulable");
            command.PreviousAttemptsToHandleCount.Should().Be(initialPublishCount + 1, "because the publish should should have been incremented");

            mockPublisher.Verify(publisher => publisher.PublishAsync(It.Is <SubmitOrderForProduction>(value => value == command), It.Is <Instant?>(value => value.HasValue)),
                                 Times.Once,
                                 "The command should have been published");
        }
Exemple #6
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="OrderProcessorFunctions"/> class.
        /// </summary>
        ///
        /// <param name="jsonSerializer">The JSON serializer to use for parsing messages from the queue.</param>
        /// <param name="retryThresholds">The thresholds for use when retrying commands on a backoff policy.</param>
        /// <param name="clock">The clock to use for time-realated operations.</param>
        /// <param name="orderProcessor">The processor to use for the order associated with a ProcesOrder command,</param>
        /// <param name="processOrderPublisher">The publisher to use for sending of the <see cref="ProcessOrder" /> command</param>
        /// <param name="submitOrderForProductionPublisher">The publisher to use for sending of the <see cref="SubmitOrderForProduction" /> command</param>
        /// <param name="notifyOfFatalFailurePublisher">The publisher to use for sending of the <see cref="NotifyOfFatalFailure" /> command.</param>
        /// <param name="eventPublisher">The publisher to use for the sending of events.</param>
        /// <param name="logger">The logger to be used for emitting telemetry from the controller.</param>
        /// <param name="lifetimeScope">The lifetime scope associated with the class; this will be disposed when the class is by the base class..</param>
        ///
        public OrderProcessorFunctions(JsonSerializer jsonSerializer,
                                       CommandRetryThresholds retryThresholds,
                                       IClock clock,
                                       IOrderProcessor orderProcessor,
                                       ICommandPublisher <ProcessOrder> processOrderPublisher,
                                       ICommandPublisher <SubmitOrderForProduction> submitOrderForProductionPublisher,
                                       ICommandPublisher <NotifyOfFatalFailure> notifyOfFatalFailurePublisher,
                                       IEventPublisher <EventBase> eventPublisher,
                                       ILogger logger,
                                       IDisposable lifetimeScope) : base(lifetimeScope)
        {
            this.jsonSerializer                    = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer));
            this.retryThresholds                   = retryThresholds ?? throw new ArgumentNullException(nameof(retryThresholds));
            this.clock                             = clock ?? throw new ArgumentNullException(nameof(clock));
            this.orderProcessor                    = orderProcessor ?? throw new ArgumentNullException(nameof(orderProcessor));
            this.processOrderPublisher             = processOrderPublisher ?? throw new ArgumentNullException(nameof(processOrderPublisher));
            this.submitOrderForProductionPublisher = submitOrderForProductionPublisher ?? throw new ArgumentNullException(nameof(submitOrderForProductionPublisher));
            this.notifyOfFatalFailurePublisher     = notifyOfFatalFailurePublisher ?? throw new ArgumentNullException(nameof(notifyOfFatalFailurePublisher));
            this.eventPublisher                    = eventPublisher ?? throw new ArgumentNullException(nameof(eventPublisher));
            this.Log = logger ?? throw new ArgumentNullException(nameof(logger));

            this.rng = new Random();
        }
Exemple #7
0
 public new Task <bool> ScheduleCommandForRetryIfEligibleAsync <TCommand>(TCommand command,
                                                                          CommandRetryThresholds retryThresholds,
                                                                          Random rng,
                                                                          IClock clock,
                                                                          ICommandPublisher <TCommand> commandPublisher) where TCommand : CommandBase =>
 base.ScheduleCommandForRetryIfEligibleAsync(command, retryThresholds, rng, clock, commandPublisher);
        /// <summary>
        ///   Schedules a command for retry, if it is eligible for doing so.
        /// </summary>
        ///
        /// <typeparam name="TCommand">The type of the command to be scheduled.</typeparam>
        ///
        /// <param name="command">The command to be .</param>
        /// <param name="retryThresholds">The retry thresholds to apply when assigning the retry backoff.</param>
        /// <param name="rng">The random number generator to use for computing retry jitter.</param>
        /// <param name="clock">The clock to use for calculating the retry delay.</param>
        /// <param name="commandPublisher">The publisher to use for scheduling the command to be retried.</param>
        ///
        /// <returns><c>true</c> if the command was scheduled for retry; <c>false</c> if it was not eligible for retry.</returns>
        ///
        /// <remarks>
        ///     If scheduled to be retried, the incoming <paramref name="command"/> will have it's <see cref="CommandBase.PreviousAttemptsToHandleCount" />
        ///     incremented by this method.  Otherwise, it will be unaltered.
        /// </remarks>
        ///
        protected virtual async Task <bool> ScheduleCommandForRetryIfEligibleAsync <TCommand>(TCommand command,
                                                                                              CommandRetryThresholds retryThresholds,
                                                                                              Random rng,
                                                                                              IClock clock,
                                                                                              ICommandPublisher <TCommand> commandPublisher) where TCommand : CommandBase
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            if (retryThresholds == null)
            {
                throw new ArgumentNullException(nameof(retryThresholds));
            }

            if (rng == null)
            {
                throw new ArgumentNullException(nameof(rng));
            }

            if (clock == null)
            {
                throw new ArgumentNullException(nameof(clock));
            }

            if (commandPublisher == null)
            {
                throw new ArgumentNullException(nameof(commandPublisher));
            }

            // If the command is out of retries, then take no further action.

            var attempts = command.PreviousAttemptsToHandleCount;

            if (attempts >= retryThresholds.CommandRetryMaxCount)
            {
                return(false);
            }

            ++attempts;
            command.PreviousAttemptsToHandleCount = attempts;

            // Publish the retry using an exponential backoff with random jitter.

            var retryInstant = clock.GetCurrentInstant().Plus(Duration.FromSeconds((Math.Pow(2, attempts) * retryThresholds.CommandRetryExponentialSeconds) + (rng.NextDouble() * retryThresholds.CommandRetryJitterSeconds)));
            await commandPublisher.PublishAsync(command, retryInstant);

            return(true);
        }