Ejemplo n.º 1
0
 private void CreateNextDestinationContextFromHeaders(MessageBrokerContext messageContext)
 {
     if (!string.IsNullOrWhiteSpace(NextDestinationPath))
     {
         var nextDestinationContext = new RoutingContext(NextDestinationPath, messageContext.Container);
         messageContext.Container.Include(nextDestinationContext);
     }
 }
Ejemplo n.º 2
0
 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);
     }
 }
Ejemplo n.º 3
0
 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);
     }
 }
Ejemplo n.º 4
0
 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);
     }
 }
Ejemplo n.º 5
0
 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);
     }
 }
Ejemplo n.º 6
0
 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);
     }
 }
Ejemplo n.º 7
0
 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();
     }
 }
Ejemplo n.º 8
0
        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();
            }
        }
Ejemplo n.º 9
0
        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;
            }
        }
Ejemplo n.º 10
0
 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);
     }
 }
Ejemplo n.º 11
0
        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();
            }
        }
Ejemplo n.º 12
0
        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);
        }
Ejemplo n.º 13
0
        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);
        }
Ejemplo n.º 14
0
        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);
            }
        }
Ejemplo n.º 15
0
        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();
            }
        }
Ejemplo n.º 16
0
        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>();
        }
Ejemplo n.º 17
0
        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);
        }
Ejemplo n.º 18
0
        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);
        }
Ejemplo n.º 19
0
        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);
        }
Ejemplo n.º 20
0
        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);
        }
Ejemplo n.º 21
0
        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));
            }
        }
Ejemplo n.º 22
0
        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);
            }
        }