async Task InnerProcessMessages(BackoffStrategy backoffStrategy)
        {
            while (!cancellationTokenSource.IsCancellationRequested)
            {
                try
                {
                    var retrieved = await messageReceiver.Receive(backoffStrategy, cancellationTokenSource.Token).ConfigureAwait(false);

                    circuitBreaker.Success();

                    foreach (var message in retrieved)
                    {
                        await concurrencyLimiter.WaitAsync(cancellationToken).ConfigureAwait(false);

                        if (cancellationTokenSource.IsCancellationRequested)
                        {
                            return;
                        }

                        InnerReceive(message).Ignore();
                    }
                }
                catch (OperationCanceledException)
                {
                    // For graceful shutdown purposes
                    return;
                }
                catch (Exception ex)
                {
                    Logger.Warn("Receiving from the queue failed", ex);
                    await circuitBreaker.Failure(ex).ConfigureAwait(false);
                }
            }
        }
        public Task StartReceive(CancellationToken cancellationToken = default)
        {
            maximumConcurrency = limitations.MaxConcurrency;
            concurrencyLimiter = new SemaphoreSlim(maximumConcurrency, maximumConcurrency);
            messagePumpCancellationTokenSource       = new CancellationTokenSource();
            messageProcessingCancellationTokenSource = new CancellationTokenSource();

            circuitBreaker = new RepeatedFailuresOverTimeCircuitBreaker("AzureStorageQueue-MessagePump", TimeToWaitBeforeTriggering, ex => criticalErrorAction("Failed to receive message from Azure Storage Queue.", ex, messageProcessingCancellationTokenSource.Token));

            Logger.DebugFormat($"Starting MessageReceiver {Id} with max concurrency: {0}", maximumConcurrency);

            var receiverConfigurations = MessagePumpHelpers.DetermineReceiverConfiguration(receiveBatchSize, degreeOfReceiveParallelism, maximumConcurrency);

            messagePumpTasks = new Task[receiverConfigurations.Count];

            for (var i = 0; i < receiverConfigurations.Count; i++)
            {
                var backoffStrategy     = new BackoffStrategy(peekInterval, maximumWaitTime);
                var batchSizeForReceive = receiverConfigurations[i].BatchSize;

                // Task.Run() so the call returns immediately instead of waiting for the first await or return down the call stack
                messagePumpTasks[i] = Task.Run(() => PumpMessagesAndSwallowExceptions(batchSizeForReceive, backoffStrategy, messagePumpCancellationTokenSource.Token), CancellationToken.None);
            }

            return(Task.CompletedTask);
        }
예제 #3
0
 public DelayedMessagesPoller(CloudTable delayedDeliveryTable, string connectionString, string errorQueue, bool isAtMostOnce, Dispatcher dispatcher, BackoffStrategy backoffStrategy)
 {
     this.errorQueue           = errorQueue;
     this.isAtMostOnce         = isAtMostOnce;
     this.delayedDeliveryTable = delayedDeliveryTable;
     this.connectionString     = connectionString;
     this.dispatcher           = dispatcher;
     this.backoffStrategy      = backoffStrategy;
 }
예제 #4
0
 async Task ProcessMessages(int batchSizeForReceive, BackoffStrategy backoffStrategy)
 {
     while (!cancellationToken.IsCancellationRequested)
     {
         try
         {
             await InnerProcessMessages(batchSizeForReceive, backoffStrategy).ConfigureAwait(false);
         }
         catch (Exception ex)
         {
             Logger.Error("Polling Dequeue Strategy failed", ex);
         }
     }
 }
예제 #5
0
 public NativeDelayedDeliveryProcessor(
     Dispatcher dispatcher,
     CloudTable delayedMessageStorageTable,
     BlobServiceClient blobServiceClient,
     string errorQueueAddress,
     TransportTransactionMode transportTransactionMode,
     BackoffStrategy backoffStrategy)
 {
     enabled         = true;
     this.dispatcher = dispatcher;
     this.delayedMessageStorageTable = delayedMessageStorageTable;
     this.blobServiceClient          = blobServiceClient;
     this.errorQueueAddress          = errorQueueAddress;
     this.transportTransactionMode   = transportTransactionMode;
     this.backoffStrategy            = backoffStrategy;
 }
        async Task PumpMessages(int batchSizeForReceive, BackoffStrategy backoffStrategy, CancellationToken pumpCancellationToken)
        {
            var receivedMessages = new List <MessageRetrieved>(batchSizeForReceive);

            if (Logger.IsDebugEnabled)
            {
                Logger.DebugFormat("Fetching {0} messages", batchSizeForReceive);
            }

            while (true)
            {
                pumpCancellationToken.ThrowIfCancellationRequested();

#pragma warning disable PS0021 // Highlight when a try block passes multiple cancellation tokens - justification:
                // The message processing cancellation token is used for processing,
                // since we only want that to be canceled when the public token passed to Stop() is canceled.
                // The message pump token is being used elsewhere, because we want those operations to be canceled as soon as Stop() is called.
                // The catch clause is correctly filtered on the message pump cancellation token.
                try
#pragma warning restore PS0021 // Highlight when a try block passes multiple cancellation tokens
                {
                    await azureMessageQueueReceiver.Receive(batchSizeForReceive, receivedMessages, backoffStrategy, pumpCancellationToken).ConfigureAwait(false);

                    circuitBreaker.Success();

                    foreach (var message in receivedMessages)
                    {
                        await concurrencyLimiter.WaitAsync(pumpCancellationToken).ConfigureAwait(false);

                        // no Task.Run() here to avoid a closure
                        _ = ProcessMessageSwallowExceptionsAndReleaseConcurrencyLimiter(message, messageProcessingCancellationTokenSource.Token);
                    }
                }
                catch (Exception ex) when(!ex.IsCausedBy(pumpCancellationToken))
                {
                    Logger.Warn("Receiving from the queue failed", ex);
                    await circuitBreaker.Failure(ex, pumpCancellationToken).ConfigureAwait(false);
                }
                finally
                {
                    receivedMessages.Clear();
                }
            }
        }
예제 #7
0
        public void Start(PushRuntimeSettings limitations)
        {
            maximumConcurrency      = limitations.MaxConcurrency;
            concurrencyLimiter      = new SemaphoreSlim(maximumConcurrency, maximumConcurrency);
            cancellationTokenSource = new CancellationTokenSource();

            var receiverConfigurations = MessagePumpHelpers.DetermineReceiverConfiguration(receiveBatchSize, degreeOfReceiveParallelism, maximumConcurrency);

            messagePumpTasks = new Task[receiverConfigurations.Count];

            cancellationToken = cancellationTokenSource.Token;

            for (var i = 0; i < receiverConfigurations.Count; i++)
            {
                var backoffStrategy     = new BackoffStrategy(peekInterval, maximumWaitTime);
                var batchSizeForReceive = receiverConfigurations[i].BatchSize;
                messagePumpTasks[i] = Task.Run(() => ProcessMessages(batchSizeForReceive, backoffStrategy), CancellationToken.None);
            }
        }
        public void Start(PushRuntimeSettings limitations)
        {
            maximumConcurrency      = limitations.MaxConcurrency;
            concurrencyLimiter      = new SemaphoreSlim(maximumConcurrency, maximumConcurrency);
            cancellationTokenSource = new CancellationTokenSource();

            if (!degreeOfReceiveParallelism.HasValue)
            {
                degreeOfReceiveParallelism = EstimateDegreeOfReceiveParallelism(maximumConcurrency);
            }

            messagePumpTasks = new Task[degreeOfReceiveParallelism.Value];

            cancellationToken = cancellationTokenSource.Token;

            for (var i = 0; i < degreeOfReceiveParallelism; i++)
            {
                var backoffStrategy = new BackoffStrategy(peekInterval, maximumWaitTime);
                messagePumpTasks[i] = Task.Run(() => ProcessMessages(backoffStrategy), CancellationToken.None);
            }
        }
예제 #9
0
        internal async Task <List <MessageRetrieved> > Receive(BackoffStrategy backoffStrategy, CancellationToken token)
        {
            var rawMessages = await inputQueue.GetMessagesAsync(BatchSize, MessageInvisibleTime, null, null, token).ConfigureAwait(false);

            var messageFound = false;
            List <MessageRetrieved> messages = null;

            foreach (var rawMessage in rawMessages)
            {
                if (!messageFound)
                {
                    messages     = new List <MessageRetrieved>(BatchSize);
                    messageFound = true;
                }

                messages.Add(new MessageRetrieved(unwrapper, rawMessage, inputQueue, errorQueue));
            }

            await backoffStrategy.OnBatch(messageFound?messages.Count : 0, token).ConfigureAwait(false);

            return(messageFound ? messages : noMessagesFound);
        }
        async Task PumpMessagesAndSwallowExceptions(int batchSizeForReceive, BackoffStrategy backoffStrategy, CancellationToken pumpCancellationToken)
        {
            Logger.Debug("Pumping messages");

            while (!pumpCancellationToken.IsCancellationRequested)
            {
                try
                {
                    await PumpMessages(batchSizeForReceive, backoffStrategy, pumpCancellationToken).ConfigureAwait(false);
                }
                catch (Exception ex) when(ex.IsCausedBy(pumpCancellationToken))
                {
                    // private token, receiver is being canceled, log exception in case stack trace is ever needed for debugging
                    Logger.Debug("Operation canceled while stopping message receiver.", ex);
                    break;
                }
                catch (Exception ex)
                {
                    Logger.Error("Polling Dequeue Strategy failed", ex);
                }
            }
        }
 public DelayedMessagesPoller(CloudTable delayedDeliveryTable, BlobServiceClient blobServiceClient, string errorQueueAddress, bool isAtMostOnce, Dispatcher dispatcher, BackoffStrategy backoffStrategy)
 {
     this.errorQueueAddress    = errorQueueAddress;
     this.isAtMostOnce         = isAtMostOnce;
     this.delayedDeliveryTable = delayedDeliveryTable;
     this.blobServiceClient    = blobServiceClient;
     this.dispatcher           = dispatcher;
     this.backoffStrategy      = backoffStrategy;
 }
예제 #12
0
        internal async Task Receive(int batchSize, List <MessageRetrieved> receivedMessages, BackoffStrategy backoffStrategy, CancellationToken token)
        {
            var rawMessages = await inputQueue.GetMessagesAsync(batchSize, MessageInvisibleTime, null, null, token).ConfigureAwait(false);

            // ReSharper disable once LoopCanBeConvertedToQuery
            foreach (var rawMessage in rawMessages)
            {
                receivedMessages.Add(new MessageRetrieved(unwrapper, rawMessage, inputQueue, errorQueue));
            }

            await backoffStrategy.OnBatch(receivedMessages.Count, token).ConfigureAwait(false);
        }