public void decreases_on_penalize() { using (var sut = new DynamicThrottling(100, 10, 3, 5, 1, 8000)) { IncreaseDegreesOfParallelism(sut); sut.NotifyWorkStarted(); var startingValue = sut.AvailableDegreesOfParallelism; sut.Penalize(); Assert.True(startingValue > sut.AvailableDegreesOfParallelism); } }
public void decreases_on_penalize() { using (var sut = new DynamicThrottling(100, 10, 3, 5, 1, 8000)) { IncreaseDegreesOfParallelism(sut); sut.NotifyWorkStarted(); var startingValue = sut.AvailableDegreesOfParallelism; sut.Penalize(); Assert.IsTrue(startingValue > sut.AvailableDegreesOfParallelism); } }
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> /// Initializes a new instance of the <see cref="SessionSubscriptionReceiver" /> class, /// automatically creating the topic and subscription if they don't exist. /// </summary> protected SessionSubscriptionReceiver(ServiceBusSettings settings, string topic, string subscription, bool requiresSequentialProcessing, ISessionSubscriptionReceiverInstrumentation instrumentation, RetryStrategy backgroundRetryStrategy) { this.settings = settings; this.topic = topic; this.subscription = subscription; this.requiresSequentialProcessing = requiresSequentialProcessing; this.instrumentation = instrumentation; tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(settings.TokenIssuer, settings.TokenAccessKey); serviceUri = ServiceBusEnvironment.CreateServiceUri(settings.ServiceUriScheme, settings.ServiceNamespace, settings.ServicePath); var messagingFactory = MessagingFactory.Create(serviceUri, tokenProvider); client = messagingFactory.CreateSubscriptionClient(topic, subscription); if (this.requiresSequentialProcessing) { client.PrefetchCount = 10; } else { client.PrefetchCount = 15; } dynamicThrottling = new DynamicThrottling( 160, 30, 3, 5, 1, 10000); receiveRetryPolicy = new RetryPolicy <ServiceBusTransientErrorDetectionStrategy>(backgroundRetryStrategy); receiveRetryPolicy.Retrying += (s, e) => { dynamicThrottling.Penalize(); Trace.TraceWarning( "An error occurred in attempt number {1} to receive a message from subscription {2}: {0}", e.LastException.Message, e.CurrentRetryCount, this.subscription); }; }
/// <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(); }