Exemplo n.º 1
0
        private Task CloseSession(bool success, IMessageSession session, CancellationToken cancellationToken)
        {
            return(receiveRetryPolicy.ExecuteAsync(
                       async() =>
            {
                try
                {
                    await session.CloseAsync();

                    if (success)
                    {
                        dynamicThrottling.NotifyWorkCompleted();
                    }
                    else
                    {
                        dynamicThrottling.NotifyWorkCompletedWithError();
                    }

                    return Task.CompletedTask;
                }
                catch (Exception ex)
                {
                    logger.LogError($"An unrecoverable error occurred while trying to CLOSE a session in subscription \"{subscription}\":\r\n{ex.Message}");
                    dynamicThrottling.NotifyWorkCompletedWithError();
                    return Task.CompletedTask;
                }
            }));
        }
Exemplo n.º 2
0
        private void ProcessPartition(string key)
        {
            //this.instrumentation.EventPublisherStarted();

            this.queue.GetPendingAsync(
                key,
                (results, hasMoreResults) =>
            {
                var enumerator = results.GetEnumerator();
                this.SendAndDeletePending(
                    enumerator,
                    allElementWereProcessed =>
                {
                    enumerator.Dispose();
                    if (!allElementWereProcessed)
                    {
                        this.EnqueueIfNotExists(key);
                    }
                    else if (hasMoreResults)
                    {
                        // if there are more events in this partition, then continue processing and do not mark work as completed.
                        ProcessPartition(key);
                        return;
                    }

                    // all elements were processed or should be retried later. Mark this job as done.
                    this.dynamicThrottling.NotifyWorkCompleted();
                    //this.instrumentation.EventPublisherFinished();
                },
                    ex =>
                {
                    enumerator.Dispose();
                    logger.LogError("An error occurred while publishing events for partition {0}:\r\n{1}", key, ex);

                    // if there was ANY unhandled error, re-add the item to collection.
                    EnqueueIfNotExists(key);
                    dynamicThrottling.NotifyWorkCompletedWithError();
                    //instrumentation.EventPublisherFinished();
                });
            },
                ex =>
            {
                logger.LogError("An error occurred while getting the events pending for publishing for partition {0}:\r\n{1}", key, ex);

                // if there was ANY unhandled error, re-add the item to collection.
                EnqueueIfNotExists(key);
                dynamicThrottling.NotifyWorkCompletedWithError();
                //instrumentation.EventPublisherFinished();
            });
        }
Exemplo n.º 3
0
        private async Task ReleaseMessage(Message msg, MessageReleaseAction releaseAction, Stopwatch roundtripStopwatch)
        {
            switch (releaseAction.Kind)
            {
            case MessageReleaseActionKind.Complete:
                await msg.SafeCompleteAsync(
                    this.subscription,
                    client,
                    success =>
                {
                    if (success)
                    {
                        this.dynamicThrottling.NotifyWorkCompleted();
                    }
                    else
                    {
                        this.dynamicThrottling.NotifyWorkCompletedWithError();
                    }
                },
                    logger,
                    roundtripStopwatch);

                break;

            case MessageReleaseActionKind.Abandon:
                await msg.SafeAbandonAsync(
                    this.subscription,
                    client,
                    success => {
                    dynamicThrottling.NotifyWorkCompletedWithError();
                },
                    logger,
                    roundtripStopwatch);

                break;

            case MessageReleaseActionKind.DeadLetter:
                await msg.SafeDeadLetterAsync(
                    this.subscription,
                    client,
                    releaseAction.DeadLetterReason,
                    releaseAction.DeadLetterDescription,
                    success => {
                    this.dynamicThrottling.NotifyWorkCompletedWithError();
                },
                    logger,
                    roundtripStopwatch);

                break;

            default:
                break;
            }
        }
        public void penalize_decreases_less_than_completed_with_error()
        {
            using (var sut1 = new DynamicThrottling(100, 10, 3, 5, 1, 8000))
            using (var sut2 = new DynamicThrottling(100, 10, 3, 5, 1, 8000))
            {
                IncreaseDegreesOfParallelism(sut1);
                IncreaseDegreesOfParallelism(sut2);

                sut1.NotifyWorkStarted();
                sut2.NotifyWorkStarted();

                sut1.Penalize();
                sut2.NotifyWorkCompletedWithError();

                Assert.True(sut1.AvailableDegreesOfParallelism > sut2.AvailableDegreesOfParallelism);
            }
        }
Exemplo n.º 5
0
        public void penalize_decreases_less_than_completed_with_error()
        {
            using (var sut1 = new DynamicThrottling(100, 10, 3, 5, 1, 8000))
                using (var sut2 = new DynamicThrottling(100, 10, 3, 5, 1, 8000))
                {
                    IncreaseDegreesOfParallelism(sut1);
                    IncreaseDegreesOfParallelism(sut2);

                    sut1.NotifyWorkStarted();
                    sut2.NotifyWorkStarted();

                    sut1.Penalize();
                    sut2.NotifyWorkCompletedWithError();

                    Assert.True(sut1.AvailableDegreesOfParallelism > sut2.AvailableDegreesOfParallelism);
                }
        }
        /// <summary>
        ///     Receives the messages in an asynchronous loop and closes the session once there are no more messages.
        /// </summary>
        private void ReceiveMessagesAndCloseSession(MessageSession session, CancellationToken cancellationToken)
        {
            var unreleasedMessages = new CountdownEvent(1);

            Action <bool> closeSession = success => {
                Action doClose = () => {
                    try {
                        unreleasedMessages.Signal();
                        if (!unreleasedMessages.Wait(15000, cancellationToken))
                        {
                            Trace.TraceWarning("Waited for pending unreleased messages before closing session in subscription {0} but they did not complete in time", subscription);
                        }
                    } catch (OperationCanceledException) { } finally {
                        unreleasedMessages.Dispose();
                    }

                    receiveRetryPolicy.ExecuteAction(
                        cb => session.BeginClose(cb, null),
                        session.EndClose,
                        () => {
                        instrumentation.SessionEnded();
                        if (success)
                        {
                            dynamicThrottling.NotifyWorkCompleted();
                        }
                        else
                        {
                            dynamicThrottling.NotifyWorkCompletedWithError();
                        }
                    },
                        ex => {
                        instrumentation.SessionEnded();
                        Trace.TraceError("An unrecoverable error occurred while trying to close a session in subscription {1}:\r\n{0}", ex, subscription);
                        dynamicThrottling.NotifyWorkCompletedWithError();
                    });
                };

                if (requiresSequentialProcessing)
                {
                    doClose.Invoke();
                }
                else
                {
                    // Allow some time for releasing the messages before closing. Also, continue in a non I/O completion thread in order to block.
                    TaskEx.Delay(200).ContinueWith(t => doClose());
                }
            };

            // Declare an action to receive the next message in the queue or closes the session if cancelled.
            Action receiveNext = null;

            // Declare an action acting as a callback whenever a non-transient exception occurs while receiving or processing messages.
            Action <Exception> recoverReceive = null;

            // Declare an action responsible for the core operations in the message receive loop.
            Action receiveMessage = () => {
                // Use a retry policy to execute the Receive action in an asynchronous and reliable fashion.
                receiveRetryPolicy.ExecuteAction
                (
                    cb => {
                    // Start receiving a new message asynchronously.
                    // Does not wait for new messages to arrive in a session. If no further messages we will just close the session.
                    session.BeginReceive(TimeSpan.Zero, cb, null);
                },
                    // Complete the asynchronous operation. This may throw an exception that will be handled internally by retry policy.
                    session.EndReceive,
                    msg => {
                    // Process the message once it was successfully received
                    // Check if we actually received any messages.
                    if (msg != null)
                    {
                        var roundtripStopwatch             = Stopwatch.StartNew();
                        long schedulingElapsedMilliseconds = 0;
                        long processingElapsedMilliseconds = 0;

                        unreleasedMessages.AddCount();

                        Task.Factory.StartNew(() => {
                            var releaseAction = MessageReleaseAction.AbandonMessage;

                            try {
                                instrumentation.MessageReceived();

                                schedulingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds;

                                // Make sure the process was told to stop receiving while it was waiting for a new message.
                                if (!cancellationToken.IsCancellationRequested)
                                {
                                    try {
                                        try {
                                            // Process the received message.
                                            releaseAction = InvokeMessageHandler(msg);

                                            processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds;
                                            instrumentation.MessageProcessed(releaseAction.Kind == MessageReleaseActionKind.Complete, processingElapsedMilliseconds);
                                        } catch {
                                            processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds;
                                            instrumentation.MessageProcessed(false, processingElapsedMilliseconds);

                                            throw;
                                        }
                                    } finally {
                                        if (roundtripStopwatch.Elapsed > TimeSpan.FromSeconds(45))
                                        {
                                            dynamicThrottling.Penalize();
                                        }
                                    }
                                }
                            } finally {
                                // Ensure that any resources allocated by a BrokeredMessage instance are released.
                                if (requiresSequentialProcessing)
                                {
                                    ReleaseMessage(msg, releaseAction, () => { receiveNext(); }, () => { closeSession(false); }, unreleasedMessages, processingElapsedMilliseconds,
                                                   schedulingElapsedMilliseconds, roundtripStopwatch);
                                }
                                else
                                {
                                    // Receives next without waiting for the message to be released.
                                    ReleaseMessage(msg, releaseAction, () => { }, () => { dynamicThrottling.Penalize(); }, unreleasedMessages, processingElapsedMilliseconds,
                                                   schedulingElapsedMilliseconds, roundtripStopwatch);
                                    receiveNext.Invoke();
                                }
                            }
                        });
                    }
                    else
                    {
                        // no more messages in the session, close it and do not continue receiving
                        closeSession(true);
                    }
                },
                    ex => {
                    // Invoke a custom action to indicate that we have encountered an exception and
                    // need further decision as to whether to continue receiving messages.
                    recoverReceive.Invoke(ex);
                });
            };

            // Initialize an action to receive the next message in the queue or closes the session if cancelled.
            receiveNext = () => {
                if (!cancellationToken.IsCancellationRequested)
                {
                    // Continue receiving and processing new messages until told to stop.
                    receiveMessage.Invoke();
                }
                else
                {
                    closeSession(true);
                }
            };

            // Initialize a custom action acting as a callback whenever a non-transient exception occurs while receiving or processing messages.
            recoverReceive = ex => {
                // Just log an exception. Do not allow an unhandled exception to terminate the message receive loop abnormally.
                Trace.TraceError("An unrecoverable error occurred while trying to receive a new message from subscription {1}:\r\n{0}", ex, subscription);

                // Cannot continue to receive messages from this session.
                closeSession(false);
            };

            // Start receiving messages asynchronously for the session.
            receiveNext.Invoke();
        }
        /// <summary>
        ///     Receives the messages in an endless asynchronous loop.
        /// </summary>
        private void ReceiveMessages(CancellationToken cancellationToken)
        {
            // Declare an action to receive the next message in the queue or end if cancelled.
            Action receiveNext = null;

            // Declare an action acting as a callback whenever a non-transient exception occurs while receiving or processing messages.
            Action <Exception> recoverReceive = null;

            // Declare an action responsible for the core operations in the message receive loop.
            Action receiveMessage = () => {
                // Use a retry policy to execute the Receive action in an asynchronous and reliable fashion.
                receiveRetryPolicy.ExecuteAction
                (
                    cb => {
                    // Start receiving a new message asynchronously.
                    client.BeginReceive(ReceiveLongPollingTimeout, cb, null);
                },
                    ar => {
                    // Complete the asynchronous operation. This may throw an exception that will be handled internally by retry policy.
                    try {
                        return(client.EndReceive(ar));
                    } catch (TimeoutException) {
                        // TimeoutException is not just transient but completely expected in this case, so not relying on Topaz to retry
                        return(null);
                    }
                },
                    msg => {
                    // Process the message once it was successfully received
                    if (processInParallel)
                    {
                        // Continue receiving and processing new messages asynchronously
                        Task.Factory.StartNew(receiveNext);
                    }

                    // Check if we actually received any messages.
                    if (msg != null)
                    {
                        var roundtripStopwatch             = Stopwatch.StartNew();
                        long schedulingElapsedMilliseconds = 0;
                        long processingElapsedMilliseconds = 0;

                        Task.Factory.StartNew(() => {
                            var releaseAction = MessageReleaseAction.AbandonMessage;

                            try {
                                instrumentation.MessageReceived();

                                schedulingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds;

                                // Make sure the process was told to stop receiving while it was waiting for a new message.
                                if (!cancellationToken.IsCancellationRequested)
                                {
                                    try {
                                        try {
                                            // Process the received message.
                                            releaseAction = InvokeMessageHandler(msg);

                                            processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds;
                                            instrumentation.MessageProcessed(releaseAction.Kind == MessageReleaseActionKind.Complete, processingElapsedMilliseconds);
                                        } catch {
                                            processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds;
                                            instrumentation.MessageProcessed(false, processingElapsedMilliseconds);

                                            throw;
                                        }
                                    } finally {
                                        if (roundtripStopwatch.Elapsed > TimeSpan.FromSeconds(45))
                                        {
                                            dynamicThrottling.Penalize();
                                        }
                                    }
                                }
                            } finally {
                                // Ensure that any resources allocated by a BrokeredMessage instance are released.
                                ReleaseMessage(msg, releaseAction, processingElapsedMilliseconds, schedulingElapsedMilliseconds, roundtripStopwatch);
                            }

                            if (!processInParallel)
                            {
                                // Continue receiving and processing new messages until told to stop.
                                receiveNext.Invoke();
                            }
                        });
                    }
                    else
                    {
                        dynamicThrottling.NotifyWorkCompleted();
                        if (!processInParallel)
                        {
                            // Continue receiving and processing new messages until told to stop.
                            receiveNext.Invoke();
                        }
                    }
                },
                    ex => {
                    // Invoke a custom action to indicate that we have encountered an exception and
                    // need further decision as to whether to continue receiving messages.
                    recoverReceive.Invoke(ex);
                });
            };

            // Initialize an action to receive the next message in the queue or end if cancelled.
            receiveNext = () => {
                dynamicThrottling.WaitUntilAllowedParallelism(cancellationToken);
                if (!cancellationToken.IsCancellationRequested)
                {
                    dynamicThrottling.NotifyWorkStarted();
                    // Continue receiving and processing new messages until told to stop.
                    receiveMessage.Invoke();
                }
            };

            // Initialize a custom action acting as a callback whenever a non-transient exception occurs while receiving or processing messages.
            recoverReceive = ex => {
                // Just log an exception. Do not allow an unhandled exception to terminate the message receive loop abnormally.
                Trace.TraceError("An unrecoverable error occurred while trying to receive a new message from subscription {1}:\r\n{0}", ex, subscription);
                dynamicThrottling.NotifyWorkCompletedWithError();

                if (!cancellationToken.IsCancellationRequested)
                {
                    // Continue receiving and processing new messages until told to stop regardless of any exceptions.
                    TaskEx.Delay(10000).ContinueWith(t => receiveMessage.Invoke());
                }
            };

            // Start receiving messages asynchronously.
            receiveNext.Invoke();
        }