private void CreateNextDestinationContextFromHeaders(MessageBrokerContext messageContext) { if (!string.IsNullOrWhiteSpace(NextDestinationPath)) { var nextDestinationContext = new RoutingContext(NextDestinationPath, messageContext.Container); messageContext.Container.Include(nextDestinationContext); } }
private void CreateSagaContextFromHeaders(MessageBrokerContext messageContext) { if (messageContext.BrokeredMessage.ApplicationProperties.TryGetValue(ApplicationProperties.SagaId, out var sagaId)) { messageContext.BrokeredMessage.ApplicationProperties.TryGetValue(ApplicationProperties.SagaStatus, out var sagaStatus); var sagaContext = new SagaContext((string)sagaId, MessageReceiverPath, NextDestinationPath, (SagaStatusEnum)sagaStatus, parentContainer: messageContext.Container); messageContext.Container.Include(sagaContext); } }
private void CreateCompensationContextFromHeaders(MessageBrokerContext messageContext, InboundBrokeredMessage inboundMessage) { if (!string.IsNullOrWhiteSpace(CompensateDestinationPath)) { inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.FailureDetails, out var detail); inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.FailureDescription, out var description); var compensateContext = new CompensationRoutingContext(CompensateDestinationPath, (string)detail, (string)description, messageContext.Container); messageContext.Container.Include(compensateContext); } }
private static void CreateErrorContextFromHeaders(MessageBrokerContext messageContext, InboundBrokeredMessage inboundMessage) { if (inboundMessage.IsError) { inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.FailureDetails, out var reason); inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.FailureDescription, out var description); var errorContext = new ErrorContext((string)reason, (string)description); messageContext.SetFailure(errorContext); } }
private async Task <bool> TryNackWithRecoveryAsync(MessageBrokerContext messageContext, TransactionContext transactionContext, CancellationToken receiverTokenSource) { try { return(await _recoveryStrategy.ExecuteAsync(() => _infrastructureReceiver.NackMessageAsync(messageContext, transactionContext, receiverTokenSource), receiverTokenSource)); } catch (Exception e) { _logger.LogError(e, "Unable to send negative acknowledgment"); return(false); } }
private async Task <bool> TryDeadletterWithRecoveryAsync(MessageBrokerContext messageContext, TransactionContext transactionContext, Exception e, CancellationToken receiverTokenSource) { try { return(await _recoveryStrategy.ExecuteAsync(() => _infrastructureReceiver.DeadletterMessageAsync(messageContext, transactionContext, "Poisoned message received", e.ToString(), receiverTokenSource), receiverTokenSource)); } catch (Exception inner) { _logger.LogError(inner, "Unable to deadletter message"); return(false); } }
private static void CreateReplyContextFromHeaders(MessageBrokerContext messageContext, InboundBrokeredMessage inboundMessage) { if (inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.ReplyToAddress, out var replyTo)) { inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.ReplyToGroupId, out var replyToSessionId); inboundMessage.ApplicationProperties.TryGetValue(ApplicationProperties.GroupId, out var groupId); replyToSessionId = !string.IsNullOrWhiteSpace((string)replyToSessionId) ? (string)replyToSessionId : (string)groupId; var replyContext = new ReplyToRoutingContext((string)replyTo, (string)replyToSessionId, messageContext.Container); messageContext.Container.Include(replyContext); inboundMessage.ClearReplyToProperties(); } }
public async Task <bool> DeadletterMessageAsync(MessageBrokerContext context, TransactionContext transactionContext, string deadLetterReason, string deadLetterErrorDescription, CancellationToken cancellationToken) { ReceivedMessage msg = null; if (!context?.Container.TryGet(out msg) ?? false) { throw new ArgumentException($"Unable to deadletter message. No {nameof(ReceivedMessage)} contained in {nameof(context)}.", nameof(msg)); } transactionContext.Container.TryGet <SqlConnection>(out var connection); transactionContext.Container.TryGet <SqlTransaction>(out var transaction); try { var edc = new EndDialogConversationCommand(connection, msg.ConvHandle, enableCleanup: _ssbOptions.CleanupOnEndConversation, transaction: transaction); await edc.ExecuteAsync(cancellationToken); using var scope = _serviceFactory.CreateScope(); var ssbSender = scope.ServiceProvider.GetRequiredService <SqlServiceBrokerSender>(); var bodyConverter = _bodyConverterFactory.CreateBodyConverter(_ssbOptions.MessageBodyType); _localReceiverDeliveryAttempts.TryGetValue(msg.ConvHandle, out var deliveryAttempts); var headers = new Dictionary <string, object>() { [SSBMessageContext.ConversationHandle] = msg.ConvHandle, [SSBMessageContext.ServiceName] = msg.ServiceName, [MessageContext.FailureDescription] = deadLetterErrorDescription, [MessageContext.FailureDetails] = deadLetterReason, [MessageContext.InfrastructureType] = SSBMessageContext.InfrastructureType, [SSBMessageContext.MessageTypeName] = ServicesMessageTypes.ChatterBrokeredMessageType, [SSBMessageContext.ServiceContractName] = ServicesMessageTypes.ChatterServiceContract, [MessageContext.ReceiveAttempts] = deliveryAttempts }; await ssbSender.Dispatch(new OutboundBrokeredMessage(context.BrokeredMessage.MessageId, msg.Body, headers, _options.DeadLetterQueuePath, bodyConverter), transactionContext); await transaction?.CommitAsync(cancellationToken); _localReceiverDeliveryAttempts.TryRemove(msg.ConvHandle, out var _); _logger.LogTrace($"Message deadlettered."); return(true); } finally { transaction?.Dispose(); connection?.Dispose(); } }
public virtual async Task DispatchReceivedMessageAsync(TMessage payload, MessageBrokerContext messageContext, CancellationToken receiverTokenSource) { receiverTokenSource.ThrowIfCancellationRequested(); try { await _receivedMessageDispatcher.DispatchAsync(payload, messageContext, receiverTokenSource); } catch (Exception e) { _logger.LogError(e, "Error dispatching brokered message to handler(s)"); throw; } }
private async Task <bool> TryExecuteFailedRecoveryAction(MessageBrokerContext messageContext, string failureDescription, Exception exception, int deliveryCount, TransactionContext transactionContext) { try { var failureContext = new FailureContext(messageContext.BrokeredMessage, this.ErrorQueueName, failureDescription, exception, deliveryCount, transactionContext); return(await _recoveryStrategy.ExecuteAsync(async() => { await _failedRecoveryAction.ExecuteAsync(failureContext); return true; }, messageContext.CancellationToken)); } catch (Exception e) { _logger.LogError(e, "Unable to execute recovery action"); return(false); } }
public async Task <bool> NackMessageAsync(MessageBrokerContext context, TransactionContext transactionContext, CancellationToken cancellationToken) { transactionContext.Container.TryGet <SqlConnection>(out var connection); transactionContext.Container.TryGet <SqlTransaction>(out var transaction); try { await transaction?.RollbackAsync(cancellationToken); _logger.LogTrace("Message negative acknowledgment complete"); return(true); } finally { transaction?.Dispose(); connection?.Dispose(); } }
public async Task <bool> DeadletterMessageAsync(MessageBrokerContext context, TransactionContext transactionContext, string deadLetterReason, string deadLetterErrorDescription, CancellationToken cancellationToken) { if (_receiveMode != ReceiveMode.PeekLock) { return(false); } if (!context.Container.TryGet <Message>(out var msg)) { _logger.LogWarning($"Unable to deadletter message. No {nameof(Message)} contained in {nameof(context)}."); return(false); } await this.InnerReceiver.DeadLetterAsync(msg.SystemProperties.LockToken, deadLetterReason, deadLetterErrorDescription); _logger.LogTrace($"Message '{msg.MessageId}' sucessfully deadlettered"); return(true); }
public async Task <bool> NackMessageAsync(MessageBrokerContext context, TransactionContext transactionContext, CancellationToken cancellationToken) { if (_receiveMode != ReceiveMode.PeekLock) { return(false); } if (!context.Container.TryGet <Message>(out var msg)) { _logger.LogWarning($"Unable to negative acknowledge message. No {nameof(Message)} contained in {nameof(context)}."); return(false); } await this.InnerReceiver.AbandonAsync(msg.SystemProperties.LockToken, msg.UserProperties); _logger.LogTrace($"Message '{msg.MessageId}' sucessfully abandoned"); return(true); }
async Task ProcessMessageAsync(MessageBrokerContext messageContext, TransactionContext transactionContext, CancellationToken receiverTokenSource) { receiverTokenSource.ThrowIfCancellationRequested(); TMessage brokeredMessagePayload = null; var inboundMessage = messageContext.BrokeredMessage; if (transactionContext is null) { transactionContext = new TransactionContext(_options.MessageReceiverPath, _options.TransactionMode.Value); } messageContext.Container.GetOrAdd(() => transactionContext); try { brokeredMessagePayload = inboundMessage.GetMessageFromBody <TMessage>(); } catch (Exception e) { throw new PoisonedMessageException($"Unable deserialize {typeof(TMessage).Name} from message body", e); } using var localTransaction = _infrastructureReceiver.CreateLocalTransaction(transactionContext); await _recoveryStrategy.ExecuteAsync(async() => { await DispatchReceivedMessageAsync(brokeredMessagePayload, messageContext, receiverTokenSource); return(true); }, receiverTokenSource); if (!receiverTokenSource.IsCancellationRequested) { if (await TryAckWithRecoveryAsync(messageContext, transactionContext, receiverTokenSource)) { localTransaction?.Complete(); } } else { await TryNackWithRecoveryAsync(messageContext, transactionContext, receiverTokenSource); } }
public async Task <bool> AckMessageAsync(MessageBrokerContext context, TransactionContext transactionContext, CancellationToken cancellationToken) { ReceivedMessage msg = null; if (!context?.Container.TryGet(out msg) ?? false) { _logger.LogTrace($"No {nameof(ReceivedMessage)} contained in {nameof(context)}."); } transactionContext.Container.TryGet <SqlConnection>(out var connection); transactionContext.Container.TryGet <SqlTransaction>(out var transaction); try { if (msg != null) { var edc = new EndDialogConversationCommand(connection, msg.ConvHandle, enableCleanup: _ssbOptions.CleanupOnEndConversation, transaction: transaction); await edc.ExecuteAsync(cancellationToken); _localReceiverDeliveryAttempts.TryRemove(msg.ConvHandle, out var _); } else { _logger.LogTrace($"Unable end dialog conversation during message acknowledgment. {nameof(msg)} is null."); } await transaction?.CommitAsync(cancellationToken); _logger.LogTrace("Message acknowledgment complete"); return(true); } finally { transaction?.Dispose(); connection?.Dispose(); } }
public async Task StartApplication() { MessageBrokerContext.SetEnvironment(Configuration.Environment); var azureConfiguration = new AzureServiceBusConfiguration { ConnectionString = File.ReadAllText($"{AppContext.BaseDirectory}\\ConnectionString.txt") }; var logInstance = new LoggerConfiguration() .MinimumLevel.Verbose() .Enrich.FromLogContext() .Enrich.WithProperty("Environment", "Testing") .WriteTo.Seq("http://localhost:5341") .CreateLogger(); _resolver = ConfigureDependencies.Initialise() .AddAzureServiceBusTransportProvider(azureConfiguration) .AddCoreDependencies() .ScanForHandlers(Assembly.GetExecutingAssembly()) .Build(); var configurationBuilder = _resolver.GetInstance <BusConfigurationBuilder>(); await configurationBuilder .SubscribeToQueue <TestQueue>() .SubscribeToEvent <TestEventV1>(Configuration.Subscription1) .SubscribeToEvent <MultipleSubscribersEventV1>(Configuration.Subscription1) .SubscribeToEvent <MultipleSubscribersEventV1>(Configuration.Subscription2) .PublishEvent <TestEventV1>() .PublishEvent <MultipleSubscribersEventV1>() .UseLogger(logInstance) .BuildAsync(); _monitor = _resolver.GetInstance <IMonitor>(); _monitor.Start(); Bus = _resolver.GetInstance <IBus>(); }
public async Task <MessageBrokerContext> ReceiveMessageAsync(TransactionContext transactionContext, CancellationToken cancellationToken) { Message message; try { message = await this.InnerReceiver.ReceiveAsync(); } catch (ServiceBusException sbe) when(sbe.IsTransient) { _logger.LogWarning(sbe, "Failure to receive message from Azure Service Bus due to transient error"); throw; } catch (ObjectDisposedException e) when(!cancellationToken.IsCancellationRequested && _innerReceiver.IsClosedOrClosing) { lock (_syncLock) { _innerReceiver = null; } _logger.LogWarning(e, "Service Bus receiver connection was closed."); return(null); } catch (Exception e) { _logger.LogError(e, "Failure to receive message from Azure Serivce Bus"); throw; } if (message is null) { return(null); } MessageBrokerContext messageContext; IBrokeredMessageBodyConverter bodyConverter = new JsonBodyConverter(); try { bodyConverter = _bodyConverterFactory.CreateBodyConverter(message.ContentType); } catch (Exception e) { _logger.LogWarning(e, $"Error creating body converter for content type '{message.ContentType}'. Defaulting to {nameof(JsonBodyConverter)}."); } finally { message.AddUserProperty(MessageContext.TimeToLive, message.TimeToLive); message.AddUserProperty(MessageContext.ExpiryTimeUtc, message.ExpiresAtUtc); message.AddUserProperty(MessageContext.InfrastructureType, ASBMessageContext.InfrastructureType); message.AddUserProperty(MessageContext.ReceiveAttempts, message.SystemProperties.DeliveryCount); messageContext = new MessageBrokerContext(message.MessageId, message.Body, message.UserProperties, _options.MessageReceiverPath, cancellationToken, bodyConverter); messageContext.Container.Include(message); transactionContext.Container.Include(this.InnerReceiver); if (_options.TransactionMode == TransactionMode.FullAtomicityViaInfrastructure) { transactionContext.Container.Include(this.InnerReceiver.ServiceBusConnection); } } return(messageContext); }
public async Task <MessageBrokerContext> ReceiveMessageAsync(TransactionContext transactionContext, CancellationToken cancellationToken) { ReceivedMessage message = null; MessageBrokerContext messageContext = null; SqlConnection connection = null; SqlTransaction transaction = null; try { connection = new SqlConnection(_ssbOptions.ConnectionString); await connection.OpenAsync(cancellationToken); transaction = await CreateTransaction(connection, cancellationToken); } catch (Exception ex) when(ex is ArgumentException || ex is InvalidOperationException) { transaction?.Dispose(); connection?.Dispose(); throw new CriticalReceiverException("Error connecting to sql", ex); } try { message = await ReceiveAsync(connection, transaction, cancellationToken); } #if NET5_0_OR_GREATER catch (SqlException e) when(e.IsTransient) { _logger.LogWarning(e, "Failure to receive message from Sql Service Broker due to transient error"); throw; } #endif catch (SqlException e) when(e.Number == 102) { throw new CriticalReceiverException($"Unable to receive message from configured queue '{_options.MessageReceiverPath}'", e); } catch (Exception e) { _logger.LogError(e, $"Error receiving sql service broker message from queue '{_options.MessageReceiverPath}'"); throw; } finally { if (message == null) { transaction?.Dispose(); connection?.Dispose(); } } if (message is null) { await DiscardMessageAsync(connection, transaction, "Discarding null message", cancellationToken); return(null); } if (message.MessageTypeName != ServicesMessageTypes.DefaultType && message.MessageTypeName != ServicesMessageTypes.ChatterBrokeredMessageType) { await DiscardMessageAsync(connection, transaction , $"Discarding message of type '{message.MessageTypeName}'. Only messages of type '{ServicesMessageTypes.DefaultType}' or '{ServicesMessageTypes.ChatterBrokeredMessageType}' will be received." , cancellationToken); return(null); } if (message.Body == null) { await DiscardMessageAsync(connection, transaction , $"Discarding message of type '{message.MessageTypeName}' with null message body" , cancellationToken); return(null); } transactionContext.Container.Include(connection); if (_transactionMode != TransactionMode.None && transaction != null) { transactionContext.Container.Include(transaction); } _localReceiverDeliveryAttempts.AddOrUpdate(message.ConvHandle, 1, (ch, deliveryAttempts) => deliveryAttempts + 1); IBrokeredMessageBodyConverter bodyConverter = new JsonUnicodeBodyConverter(); byte[] messagePayload = message.Body; string messageId = message.ConvHandle.ToString(); IDictionary <string, object> headers = new Dictionary <string, object>(); try { bodyConverter = _bodyConverterFactory.CreateBodyConverter(_ssbOptions.MessageBodyType); if (message.MessageTypeName == ServicesMessageTypes.ChatterBrokeredMessageType) { var brokeredMessage = bodyConverter.Convert <OutboundBrokeredMessage>(message.Body); if (brokeredMessage == null) { throw new ArgumentNullException(nameof(brokeredMessage), $"Unable to deserialize {nameof(OutboundBrokeredMessage)} from message body"); } messagePayload = brokeredMessage.Body; messageId = brokeredMessage.MessageId; headers = brokeredMessage.MessageContext; } } catch (Exception e) { _logger.LogWarning(e, $"Error creating body converter for content type '{_ssbOptions.MessageBodyType}'. Defaulting to {nameof(JsonUnicodeBodyConverter)}."); } finally { _localReceiverDeliveryAttempts.TryGetValue(message.ConvHandle, out var deliveryAttempts); headers[SSBMessageContext.ConversationGroupId] = message.ConvGroupHandle; headers[SSBMessageContext.ConversationHandle] = message.ConvHandle; headers[SSBMessageContext.MessageSequenceNumber] = message.MessageSeqNo; headers[SSBMessageContext.ServiceName] = message.ServiceName; headers[SSBMessageContext.ServiceContractName] = message.ServiceContractName; headers[SSBMessageContext.MessageTypeName] = message.MessageTypeName; headers[MessageContext.InfrastructureType] = SSBMessageContext.InfrastructureType; headers[MessageContext.ReceiveAttempts] = deliveryAttempts; messageContext = new MessageBrokerContext(messageId, messagePayload, headers, _options.MessageReceiverPath, cancellationToken, bodyConverter); messageContext.Container.Include(message); } return(messageContext); }
public override async Task DispatchReceivedMessageAsync(TProcessorCommand message, MessageBrokerContext context, CancellationToken receiverTokenSource) { receiverTokenSource.ThrowIfCancellationRequested(); using var scope = _serviceFactory.CreateScope(); var dispatcher = scope.ServiceProvider.GetRequiredService <IMessageDispatcher>(); context.Container.Include((IExternalDispatcher)scope.ServiceProvider.GetRequiredService <IBrokeredMessageDispatcher>()); int totalChangeCount = message.Changes.Count(); if (totalChangeCount == 0) { _logger.LogTrace("No inserted or deleted records contained in {CommandType}", nameof(ProcessChangeFeedCommand <TRowChangeData>)); return; } int inserted = 0, updated = 0, deleted = 0; _logger.LogTrace("Processing {TotalNumChanges} changes from Change Feed", totalChangeCount); foreach (var changeFeedItem in message.Changes) { if (changeFeedItem.Inserted != null && changeFeedItem.Deleted != null) { await dispatcher.Dispatch(new RowUpdatedEvent <TRowChangeData>(changeFeedItem.Inserted, changeFeedItem.Deleted), context); _logger.LogTrace("Processed UPDATE from change feed"); updated++; } else if (changeFeedItem.Inserted != null && changeFeedItem.Deleted == null) { await dispatcher.Dispatch(new RowInsertedEvent <TRowChangeData>(changeFeedItem.Inserted), context); _logger.LogTrace("Processed INSERT from change feed"); inserted++; } else if (changeFeedItem.Inserted == null && changeFeedItem.Deleted != null) { await dispatcher.Dispatch(new RowDeletedEvent <TRowChangeData>(changeFeedItem.Deleted), context); _logger.LogTrace("Processed DELETE from change feed"); deleted++; } else { _logger.LogWarning("No changes to process in Change Feed"); } } _logger.LogTrace("Finished processing {TotalNumChanges} changes. {NumInserts} inserts, {NumUpdates} updates, {NumDeletes} deletes.", totalChangeCount, inserted, updated, deleted); }
public void StartReceiver(Func <MessageBrokerContext, TransactionContext, Task> brokeredMessageHandler, CancellationToken receiverTerminationToken) { receiverTerminationToken.Register( () => { this.InnerReceiver.CloseAsync(); _innerReceiver = null; }); var messageHandlerOptions = new MessageHandlerOptions(HandleMessageException) { AutoComplete = false, MaxConcurrentCalls = _maxConcurrentCalls }; this.InnerReceiver.RegisterMessageHandler(async(msg, receivePumpToken) => { var transactionMode = msg.GetTransactionMode(); using var scope = CreateTransactionScope(transactionMode); try { msg.AddUserProperty(ApplicationProperties.TimeToLive, msg.TimeToLive); msg.AddUserProperty(ApplicationProperties.ExpiryTimeUtc, msg.ExpiresAtUtc); var bodyConverter = _bodyConverterFactory.CreateBodyConverter(msg.ContentType); var messageContext = new MessageBrokerContext(msg.MessageId, msg.Body, msg.UserProperties, this.MessageReceiverPath, bodyConverter); messageContext.Container.Include(msg); var transactionContext = new TransactionContext(this.MessageReceiverPath, transactionMode); transactionContext.Container.Include(this.InnerReceiver); if (transactionMode == TransactionMode.FullAtomicityViaInfrastructure) { transactionContext.Container.Include(this.ServiceBusConnection); } await brokeredMessageHandler(messageContext, transactionContext).ConfigureAwait(false); await this.InnerReceiver.CompleteAsync(msg.SystemProperties.LockToken).ConfigureAwait(false); } catch (CriticalBrokeredMessageReceiverException cbmre) { await AbandonWithExponentialDelayAsync(msg).ConfigureAwait(false); _logger.LogError($"Critical error encountered receiving message. MessageId: '{msg.MessageId}, CorrelationId: '{msg.CorrelationId}'" + "\n" + $"{cbmre.ErrorContext}"); } catch (Exception e) { await AbandonWithExponentialDelayAsync(msg).ConfigureAwait(false); _logger.LogWarning($"Message with Id '{msg.MessageId}' has been abandoned:\n '{e}'"); } finally { scope?.Complete(); } }, messageHandlerOptions); }
async Task MessageReceiverLoopAsync() { try { while (!_messageReceiverLoopTokenSource.IsCancellationRequested) { await _semaphore.WaitAsync(_messageReceiverLoopTokenSource.Token); MessageBrokerContext messageContext = null; TransactionContext transactionContext = new TransactionContext(this.MessageReceiverPath, _options.TransactionMode.Value); try { _messageReceiverLoopTokenSource.Token.ThrowIfCancellationRequested(); messageContext = await _recoveryStrategy.ExecuteAsync(() => _infrastructureReceiver.ReceiveMessageAsync(transactionContext, _messageReceiverLoopTokenSource.Token), _messageReceiverLoopTokenSource.Token); if (messageContext != null) { _logger.LogTrace("Message received successfully"); await ProcessMessageAsync(messageContext, transactionContext, _messageReceiverLoopTokenSource.Token); _logger.LogTrace("Message processed successfully"); } } catch (CriticalReceiverException) { throw; //stop receiver loop } catch (OperationCanceledException) when(_messageReceiverLoopTokenSource.IsCancellationRequested) { } catch (ObjectDisposedException) when(_messageReceiverLoopTokenSource.IsCancellationRequested) { } catch (PoisonedMessageException e) { _logger.LogError(e, "Poisoned message received. Deadlettering."); await TryDeadletterWithRecoveryAsync(messageContext, transactionContext, e, _messageReceiverLoopTokenSource.Token); } catch (Exception e) { if (messageContext == null) { _logger.LogError(e, "Error receiving brokered message"); } else { _logger.LogError(e, "Error processing brokered message"); var deliveryCount = await _recoveryStrategy.ExecuteAsync(() => _infrastructureReceiver.MessageDeliveryCountAsync(messageContext, _messageReceiverLoopTokenSource.Token), _messageReceiverLoopTokenSource.Token); if (deliveryCount >= _options.MaxReceiveAttempts) { if (await TryDeadletterWithRecoveryAsync(messageContext, transactionContext, e, _messageReceiverLoopTokenSource.Token)) { await TryExecuteFailedRecoveryAction(messageContext, "Max message receive attempts exceeded", e, deliveryCount, transactionContext); } } else { await TryNackWithRecoveryAsync(messageContext, transactionContext, _messageReceiverLoopTokenSource.Token); } } } finally { try { _semaphore?.Release(); } catch (ObjectDisposedException) { } } } } catch (CriticalReceiverException e) { _logger.LogCritical(e, "Receiver is unable continue due to critical error"); await _criticalFailureNotifier.Notify(new FailureContext(null, this.ErrorQueueName, "Critical error occurred", e, -1, null)); } }
async Task ReceiveInboundBrokeredMessage(MessageBrokerContext messageContext, TransactionContext transactionContext) { try { if (messageContext is null) { throw new ArgumentNullException(nameof(messageContext), $"A {typeof(MessageBrokerContext).Name} was not created by the messaging infrastructure."); } var inboundMessage = messageContext.BrokeredMessage; messageContext.ExternalDispatcher = _brokeredMessageDispatcher; CreateErrorContextFromHeaders(messageContext, inboundMessage); CreateSagaContextFromHeaders(messageContext); CreateReplyContextFromHeaders(messageContext, inboundMessage); CreateNextDestinationContextFromHeaders(messageContext); CreateCompensationContextFromHeaders(messageContext, inboundMessage); inboundMessage.WithRouteToSelfPath(this.DestinationPath); inboundMessage.UpdateVia(Description); if (transactionContext is null) { transactionContext = new TransactionContext(MessageReceiverPath, inboundMessage.TransactionMode); } messageContext.Container.Include(transactionContext); var brokeredMessagePayload = inboundMessage.GetMessageFromBody <TMessage>(); using var scope = _serviceFactory.CreateScope(); var dispatcher = scope.ServiceProvider.GetRequiredService <IMessageDispatcher>(); await dispatcher.Dispatch(brokeredMessagePayload, messageContext).ConfigureAwait(false); inboundMessage.SuccessfullyReceived = true; } catch (CompensationRoutingException cre) { var errorContext = new ErrorContext( $"Routing compensation message of type '{typeof(TMessage).Name}' to '{cre.RoutingContext.DestinationPath?.ToString()}' failed", $"Compensation reason: '{cre.RoutingContext?.ToString()}'\n" + $"Routing failure reason: '{cre.InnerException?.Message}'"); messageContext.SetFailure(errorContext); throw new CriticalBrokeredMessageReceiverException(errorContext, cre); } catch (Exception e) { ErrorContext errorContext; if (messageContext is null) { errorContext = new ErrorContext( $"A brokered message was received with no {typeof(MessageBrokerContext).Name}", $"{e.Message}"); throw new CriticalBrokeredMessageReceiverException(errorContext, e); } errorContext = new ErrorContext( $"An error was encountered receiving message '{typeof(TMessage).Name}'", $"{e.Message}"); messageContext.SetFailure(errorContext); throw new CriticalBrokeredMessageReceiverException(errorContext, e); } }