/// <summary> /// Initializes a new instance of the <see cref="ProcessErrorEventArgs" /> class. /// </summary> /// /// <param name="exception">The exception that triggered the call to the error event handler.</param> /// <param name="errorSource">The source associated with the error.</param> /// <param name="fullyQualifiedNamespace">The endpoint used when this exception occurred.</param> /// <param name="entityPath">The entity path used when this exception occurred.</param> public ProcessErrorEventArgs( Exception exception, ServiceBusErrorSource errorSource, string fullyQualifiedNamespace, string entityPath) { Exception = exception; ErrorSource = errorSource; FullyQualifiedNamespace = fullyQualifiedNamespace; EntityPath = entityPath; }
/// <summary> /// Initializes a new instance of the <see cref="ProcessErrorEventArgs" /> class. /// </summary> /// /// <param name="exception">The exception that triggered the call to the error event handler.</param> /// <param name="errorSource">The source associated with the error.</param> /// <param name="fullyQualifiedNamespace">The endpoint used when this exception occurred.</param> /// <param name="entityPath">The entity path used when this exception occurred.</param> /// <param name="cancellationToken">The processor's <see cref="System.Threading.CancellationToken"/> instance which will be cancelled /// in the event that <see cref="ServiceBusProcessor.StopProcessingAsync"/> is called.</param> public ProcessErrorEventArgs( Exception exception, ServiceBusErrorSource errorSource, string fullyQualifiedNamespace, string entityPath, CancellationToken cancellationToken) { Exception = exception; ErrorSource = errorSource; FullyQualifiedNamespace = fullyQualifiedNamespace; EntityPath = entityPath; CancellationToken = cancellationToken; }
private static void ThrowIfSessionLockLost( Exception exception, ServiceBusErrorSource errorSource) { // we need to propagate this in order to dispose the session receiver // in the same place where we are creating them. var sbException = exception as ServiceBusException; if (sbException?.Reason == ServiceBusException.FailureReason.SessionLockLost) { sbException.ProcessorErrorSource = errorSource; throw sbException; } }
public virtual async Task ReceiveAndProcessMessagesAsync(CancellationToken cancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.Receive; try { // loop within the context of this thread while (!cancellationToken.IsCancellationRequested && !Processor.Connection.IsClosed) { errorSource = ServiceBusErrorSource.Receive; IReadOnlyList <ServiceBusReceivedMessage> messages = await Receiver.ReceiveMessagesAsync( maxMessages : 1, maxWaitTime : _maxReceiveWaitTime, isProcessor : true, cancellationToken : cancellationToken).ConfigureAwait(false); ServiceBusReceivedMessage message = messages.Count == 0 ? null : messages[0]; if (message == null) { continue; } await ProcessOneMessageWithinScopeAsync( message, DiagnosticProperty.ProcessMessageActivityName, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) // If the user manually throws a TCE, then we should log it. when(!(ex is TaskCanceledException) || !cancellationToken.IsCancellationRequested) { if (ex is ServiceBusException sbException && sbException.ProcessorErrorSource.HasValue) { errorSource = sbException.ProcessorErrorSource.Value; } await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, Processor.FullyQualifiedNamespace, Processor.EntityPath, cancellationToken)) .ConfigureAwait(false); } }
public virtual async Task ReceiveAndProcessMessagesAsync(CancellationToken cancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.Receive; try { // loop within the context of this thread while (!cancellationToken.IsCancellationRequested) { errorSource = ServiceBusErrorSource.Receive; ServiceBusReceivedMessage message = await Receiver.ReceiveAsync( _maxReceiveWaitTime, cancellationToken).ConfigureAwait(false); if (message == null) { continue; } await ProcessOneMessage( message, cancellationToken) .ConfigureAwait(false); } } catch (Exception ex) when(!(ex is TaskCanceledException)) { if (ex is ServiceBusException sbException && sbException.ProcessorErrorSource.HasValue) { errorSource = sbException.ProcessorErrorSource.Value; } await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, _fullyQualifiedNamespace, _entityPath)) .ConfigureAwait(false); } }
public override async Task ReceiveAndProcessMessagesAsync( CancellationToken cancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.Receive; try { errorSource = ServiceBusErrorSource.AcceptMessageSession; bool releaseSemaphore = false; try { try { await _concurrentAcceptSessionsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); // only attempt to release semaphore if WaitAsync is successful, // otherwise SemaphoreFullException can occur. releaseSemaphore = true; } catch (OperationCanceledException) { // propagate as TCE so it will be handled by the outer catch block throw new TaskCanceledException(); } try { await EnsureReceiverCreated(cancellationToken).ConfigureAwait(false); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusException.FailureReason.ServiceTimeout) { // these exceptions are expected when no messages are available // so simply return and allow this to be tried again on next thread return; } } finally { if (releaseSemaphore) { _concurrentAcceptSessionsSemaphore.Release(); } } // loop within the context of this thread while (!cancellationToken.IsCancellationRequested) { errorSource = ServiceBusErrorSource.Receive; ServiceBusReceivedMessage message = await _receiver.ReceiveMessageAsync( _maxReceiveWaitTime, cancellationToken).ConfigureAwait(false); if (message == null) { // Break out of the loop to allow a new session to // be processed. break; } await ProcessOneMessageWithinScopeAsync( message, DiagnosticProperty.ProcessSessionMessageActivityName, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) when(!(ex is TaskCanceledException)) { if (ex is ServiceBusException sbException && sbException.ProcessorErrorSource.HasValue) { errorSource = sbException.ProcessorErrorSource.Value; } await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, _fullyQualifiedNamespace, _entityPath)) .ConfigureAwait(false); } finally { await CloseReceiverIfNeeded(cancellationToken).ConfigureAwait(false); } }
public override async Task ReceiveAndProcessMessagesAsync(CancellationToken processorCancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.AcceptMessageSession; bool canProcess = false; try { try { canProcess = await EnsureCanProcess(processorCancellationToken).ConfigureAwait(false); if (!canProcess) { return; } } catch (ServiceBusException ex) when(ex.Reason == ServiceBusFailureReason.ServiceTimeout) { // these exceptions are expected when no messages are available // so simply return and allow this to be tried again on next thread return; } // loop within the context of this thread while (!_sessionCancellationSource.Token.IsCancellationRequested) { errorSource = ServiceBusErrorSource.Receive; ServiceBusReceivedMessage message = await _receiver.ReceiveMessageAsync( _maxReceiveWaitTime, _sessionCancellationSource.Token).ConfigureAwait(false); if (message == null) { // Break out of the loop to allow a new session to // be processed. break; } await ProcessOneMessageWithinScopeAsync( message, DiagnosticProperty.ProcessSessionMessageActivityName, _sessionCancellationSource.Token).ConfigureAwait(false); } } catch (Exception ex) when(!(ex is TaskCanceledException) || // If the user manually throws a TCE, then we should log it. (!_sessionCancellationSource.IsCancellationRequested && // Even though the _sessionCancellationSource is linked to processorCancellationToken, // we need to check both here in case the processor token gets cancelled before the // session token is linked. !processorCancellationToken.IsCancellationRequested)) { if (ex is ServiceBusException sbException && sbException.ProcessorErrorSource.HasValue) { errorSource = sbException.ProcessorErrorSource.Value; // Signal cancellation so user event handlers can stop whatever processing they are doing // as soon as we know the session lock has been lost. Note, we don't have analogous handling // for message locks in ReceiverManager, because there is only ever one thread processing a // single message at one time, so cancelling the token there would serve no purpose. if (sbException.Reason == ServiceBusFailureReason.SessionLockLost) { _sessionCancellationSource.Cancel(); } } await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, _fullyQualifiedNamespace, _entityPath)) .ConfigureAwait(false); } finally { if (canProcess) { await CloseReceiverIfNeeded( processorCancellationToken).ConfigureAwait(false); } } }
/// <summary> /// /// </summary> /// <param name="message"></param> /// <param name="cancellationToken"></param> /// <returns></returns> private async Task ProcessOneMessage( ServiceBusReceivedMessage message, CancellationToken cancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.Receive; CancellationTokenSource renewLockCancellationTokenSource = null; Task renewLock = null; try { if (!Receiver.IsSessionReceiver && Receiver.ReceiveMode == ReceiveMode.PeekLock && AutoRenewLock) { renewLockCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); renewLock = RenewMessageLock( message, renewLockCancellationTokenSource); } errorSource = ServiceBusErrorSource.UserCallback; await OnMessageHandler(message, cancellationToken).ConfigureAwait(false); if (Receiver.ReceiveMode == ReceiveMode.PeekLock && _processorOptions.AutoComplete && !message.IsSettled) { errorSource = ServiceBusErrorSource.Complete; // don't pass the processor cancellation token // as we want in flight autocompletion to be able // to finish await Receiver.CompleteMessageAsync( message.LockToken, CancellationToken.None) .ConfigureAwait(false); } await CancelTask(renewLockCancellationTokenSource, renewLock).ConfigureAwait(false); } catch (Exception ex) // This prevents exceptions relating to processing a message from bubbling up all // the way to the main thread when calling StopProcessingAsync, which we don't want // as it isn't actionable. when(!(ex is TaskCanceledException) || !cancellationToken.IsCancellationRequested) { ThrowIfSessionLockLost(ex, errorSource); await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, _fullyQualifiedNamespace, _entityPath)) .ConfigureAwait(false); // if the user settled the message, or if the message or session lock was lost, // do not attempt to abandon the message ServiceBusException.FailureReason?failureReason = (ex as ServiceBusException)?.Reason; if (!message.IsSettled && _receiverOptions.ReceiveMode == ReceiveMode.PeekLock && failureReason != ServiceBusException.FailureReason.SessionLockLost && failureReason != ServiceBusException.FailureReason.MessageLockLost) { try { // don't pass the processor cancellation token // as we want in flight abandon to be able // to finish even if user stopped processing await Receiver.AbandonMessageAsync( message.LockToken, cancellationToken : CancellationToken.None) .ConfigureAwait(false); } catch (Exception exception) { ThrowIfSessionLockLost(exception, ServiceBusErrorSource.Abandon); await RaiseExceptionReceived( new ProcessErrorEventArgs( exception, ServiceBusErrorSource.Abandon, _fullyQualifiedNamespace, _entityPath)) .ConfigureAwait(false); } } } finally { renewLockCancellationTokenSource?.Cancel(); renewLockCancellationTokenSource?.Dispose(); } }
private async Task ProcessOneMessage( ServiceBusReceivedMessage triggerMessage, CancellationToken cancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.Receive; EventArgs args = null; try { errorSource = ServiceBusErrorSource.ProcessMessageCallback; try { ServiceBusEventSource.Log.ProcessorMessageHandlerStart(Processor.Identifier, triggerMessage.SequenceNumber, triggerMessage.LockTokenGuid); args = await OnMessageHandler(triggerMessage, cancellationToken).ConfigureAwait(false); ServiceBusEventSource.Log.ProcessorMessageHandlerComplete(Processor.Identifier, triggerMessage.SequenceNumber, triggerMessage.LockTokenGuid); } catch (Exception ex) { ServiceBusEventSource.Log.ProcessorMessageHandlerException(Processor.Identifier, triggerMessage.SequenceNumber, ex.ToString(), triggerMessage.LockTokenGuid); throw; } if (Receiver.ReceiveMode == ServiceBusReceiveMode.PeekLock && ProcessorOptions.AutoCompleteMessages) { var messages = (args is ProcessMessageEventArgs processMessageEventArgs) ? processMessageEventArgs.Messages.Keys : ((ProcessSessionMessageEventArgs)args).Messages.Keys; foreach (ServiceBusReceivedMessage message in messages) { if (!message.IsSettled) { errorSource = ServiceBusErrorSource.Complete; // don't pass the processor cancellation token // as we want in flight auto-completion to be able // to finish await Receiver.CompleteMessageAsync( message, CancellationToken.None) .ConfigureAwait(false); message.IsSettled = true; } } } } catch (Exception ex) // This prevents exceptions relating to processing a message from bubbling up all // the way to the main thread when calling StopProcessingAsync, which we don't want // as it isn't actionable. when(!(ex is TaskCanceledException) || !cancellationToken.IsCancellationRequested) { ThrowIfSessionLockLost(ex, errorSource); await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, Processor.FullyQualifiedNamespace, Processor.EntityPath, cancellationToken)) .ConfigureAwait(false); // if the user settled the message, or if the message or session lock was lost, // do not attempt to abandon the message ServiceBusFailureReason?failureReason = (ex as ServiceBusException)?.Reason; if (!triggerMessage.IsSettled && _receiverOptions.ReceiveMode == ServiceBusReceiveMode.PeekLock && failureReason != ServiceBusFailureReason.SessionLockLost && failureReason != ServiceBusFailureReason.MessageLockLost) { try { // don't pass the processor cancellation token // as we want in flight abandon to be able // to finish even if user stopped processing await Receiver.AbandonMessageAsync( triggerMessage.LockTokenGuid, cancellationToken : CancellationToken.None) .ConfigureAwait(false); } catch (Exception exception) { ThrowIfSessionLockLost(exception, ServiceBusErrorSource.Abandon); await RaiseExceptionReceived( new ProcessErrorEventArgs( exception, ServiceBusErrorSource.Abandon, Processor.FullyQualifiedNamespace, Processor.EntityPath, cancellationToken)) .ConfigureAwait(false); } } } finally { if (args is ProcessMessageEventArgs processMessageEventArgs) { await processMessageEventArgs.CancelMessageLockRenewalAsync().ConfigureAwait(false); } } }
public override async Task ReceiveAndProcessMessagesAsync(CancellationToken processorCancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.AcceptSession; bool canProcess = false; try { try { canProcess = await EnsureCanProcess(processorCancellationToken).ConfigureAwait(false); if (!canProcess) { return; } } catch (ServiceBusException ex) when(ex.Reason == ServiceBusFailureReason.ServiceTimeout) { // these exceptions are expected when no messages are available // so simply return and allow this to be tried again on next thread return; } using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(processorCancellationToken, _sessionCancellationSource.Token); // loop within the context of this thread while (!linkedTokenSource.Token.IsCancellationRequested) { errorSource = ServiceBusErrorSource.Receive; IReadOnlyList <ServiceBusReceivedMessage> messages = await Receiver.ReceiveMessagesAsync( maxMessages : 1, maxWaitTime : _maxReceiveWaitTime, isProcessor : true, cancellationToken : linkedTokenSource.Token).ConfigureAwait(false); ServiceBusReceivedMessage message = messages.Count == 0 ? null : messages[0]; if (message == null) { // Break out of the loop to allow a new session to // be processed. _receiveTimeout = true; break; } await ProcessOneMessageWithinScopeAsync( message, DiagnosticProperty.ProcessSessionMessageActivityName, linkedTokenSource.Token).ConfigureAwait(false); } } catch (Exception ex) when(ex is not TaskCanceledException) { if (ex is ServiceBusException sbException) { if (sbException.ProcessorErrorSource.HasValue) { errorSource = sbException.ProcessorErrorSource.Value; } // Signal cancellation so user event handlers can stop whatever processing they are doing // as soon as we know the session lock has been lost. Note, we don't have analogous handling // for message locks in ReceiverManager, because there is only ever one thread processing a // single message at one time, so cancelling the token there would serve no purpose. if (sbException.Reason == ServiceBusFailureReason.SessionLockLost) { CancelSession(); } } await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, Processor.FullyQualifiedNamespace, Processor.EntityPath, processorCancellationToken)) .ConfigureAwait(false); } finally { if (canProcess) { await CloseReceiverIfNeeded(processorCancellationToken).ConfigureAwait(false); } } }
public override async Task ReceiveAndProcessMessagesAsync(CancellationToken cancellationToken) { ServiceBusErrorSource errorSource = ServiceBusErrorSource.AcceptMessageSession; bool canProcess = false; try { try { canProcess = await EnsureCanProcess(cancellationToken).ConfigureAwait(false); if (!canProcess) { return; } } catch (ServiceBusException ex) when(ex.Reason == ServiceBusException.FailureReason.ServiceTimeout) { // these exceptions are expected when no messages are available // so simply return and allow this to be tried again on next thread return; } // loop within the context of this thread while (!cancellationToken.IsCancellationRequested) { errorSource = ServiceBusErrorSource.Receive; ServiceBusReceivedMessage message = await _receiver.ReceiveMessageAsync( _maxReceiveWaitTime, cancellationToken).ConfigureAwait(false); if (message == null) { // Break out of the loop to allow a new session to // be processed. break; } await ProcessOneMessageWithinScopeAsync( message, DiagnosticProperty.ProcessSessionMessageActivityName, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) when(!(ex is TaskCanceledException)) { if (ex is ServiceBusException sbException && sbException.ProcessorErrorSource.HasValue) { errorSource = sbException.ProcessorErrorSource.Value; } await RaiseExceptionReceived( new ProcessErrorEventArgs( ex, errorSource, _fullyQualifiedNamespace, _entityPath)) .ConfigureAwait(false); } finally { if (canProcess) { await CloseReceiverIfNeeded(cancellationToken).ConfigureAwait(false); } } }