public async Task<int> Retry(IncomingMessage message, TimeSpan delay, TransportTransaction transportTransaction)
        {
            var outgoingMessage = new OutgoingMessage(message.MessageId, new Dictionary<string, string>(message.Headers), message.Body);

            var currentDelayedRetriesAttempt = message.GetDelayedDeliveriesPerformed() + 1;

            outgoingMessage.SetCurrentDelayedDeliveries(currentDelayedRetriesAttempt);
            outgoingMessage.SetDelayedDeliveryTimestamp(DateTime.UtcNow);

            UnicastAddressTag messageDestination;
            List<DeliveryConstraint> deliveryConstraints = null;
            if (timeoutManagerAddress == null)
            {
                // transport supports native deferred messages, directly send to input queue with delay constraint:
                deliveryConstraints = new List<DeliveryConstraint>(1)
                {
                    new DelayDeliveryWith(delay)
                };
                messageDestination = new UnicastAddressTag(endpointInputQueue);
            }
            else
            {
                // transport doesn't support native deferred messages, reroute to timeout manager:
                outgoingMessage.Headers[TimeoutManagerHeaders.RouteExpiredTimeoutTo] = endpointInputQueue;
                outgoingMessage.Headers[TimeoutManagerHeaders.Expire] = DateTimeExtensions.ToWireFormattedString(DateTime.UtcNow + delay);
                messageDestination = new UnicastAddressTag(timeoutManagerAddress);
            }

            var transportOperations = new TransportOperations(new TransportOperation(outgoingMessage, messageDestination, deliveryConstraints: deliveryConstraints));

            await dispatcher.Dispatch(transportOperations, transportTransaction, new ContextBag()).ConfigureAwait(false);

            return currentDelayedRetriesAttempt;
        }
        public override async Task ReceiveMessage()
        {
            Message message;

            if (!TryReceive(MessageQueueTransactionType.None, out message))
            {
                return;
            }

            Dictionary<string, string> headers;

            if (!TryExtractHeaders(message, out headers))
            {
                MovePoisonMessageToErrorQueue(message, IsQueuesTransactional ? MessageQueueTransactionType.Single : MessageQueueTransactionType.None);
                return;
            }

            var transportTransaction = new TransportTransaction();

            using (var bodyStream = message.BodyStream)
            {
                try
                {
                    await TryProcessMessage(message.Id, headers, bodyStream, transportTransaction).ConfigureAwait(false);
                }
                catch (Exception exception)
                {
                    message.BodyStream.Position = 0;

                    await HandleError(message, headers, exception, transportTransaction, 1).ConfigureAwait(false);
                }
            }
        }
 /// <summary>
 /// Creates a new transport receive context.
 /// </summary>
 /// <param name="receivedMessage">The received message.</param>
 /// <param name="transportTransaction">The transport transaction.</param>
 /// <param name="cancellationTokenSource">
 /// Allows the pipeline to flag that it has been aborted and the receive operation should be rolled back.
 /// It also allows the transport to communicate to the pipeline to abort if possible.
 /// </param>
 /// <param name="parentContext">The parent context.</param>
 public TransportReceiveContext(IncomingMessage receivedMessage, TransportTransaction transportTransaction, CancellationTokenSource cancellationTokenSource, IBehaviorContext parentContext)
     : base(parentContext)
 {
     this.cancellationTokenSource = cancellationTokenSource;
     Message = receivedMessage;
     Set(Message);
     Set(transportTransaction);
 }
        public async Task Should_float_transport_transaction_to_dispatcher()
        {
            var transportTransaction = new TransportTransaction();
            var delayedRetryExecutor = CreateExecutor(nativeDeferralsOn: true);
            var incomingMessage = CreateMessage();

            await delayedRetryExecutor.Retry(incomingMessage, TimeSpan.Zero, transportTransaction);

            Assert.AreEqual(dispatcher.Transaction, transportTransaction);
        }
        public Task<CompletableSynchronizedStorageSession> TryAdapt(TransportTransaction transportTransaction, ContextBag context)
        {
            Transaction ambientTransaction;

            if (transportTransaction.TryGet(out ambientTransaction))
            {
                var transaction = new InMemoryTransaction();
                CompletableSynchronizedStorageSession session = new InMemorySynchronizedStorageSession(transaction);
                ambientTransaction.EnlistVolatile(new EnlistmentNotification(transaction), EnlistmentOptions.None);
                return Task.FromResult(session);
            }
            return EmptyTask;
        }
        public async Task MoveToErrorQueue_should_dispatch_message_to_error_queue()
        {
            var customErrorQueue = "random_error_queue";
            var transportTransaction = new TransportTransaction();
            var incomingMessage = new IncomingMessage("messageId", new Dictionary<string, string>(), new byte[0]);

            await moveToErrorsExecutor.MoveToErrorQueue(customErrorQueue, incomingMessage, new Exception(), transportTransaction);

            Assert.That(dispatcher.TransportOperations.MulticastTransportOperations.Count(), Is.EqualTo(0));
            Assert.That(dispatcher.TransportOperations.UnicastTransportOperations.Count(), Is.EqualTo(1));
            Assert.That(dispatcher.Transaction, Is.EqualTo(transportTransaction));

            var outgoingMessage = dispatcher.TransportOperations.UnicastTransportOperations.Single();
            Assert.That(outgoingMessage.Destination, Is.EqualTo(customErrorQueue));
            Assert.That(outgoingMessage.Message.MessageId, Is.EqualTo(incomingMessage.MessageId));
        }
        TransportTransaction PrepareTransportTransaction(SqlConnection connection, SqlTransaction transaction)
        {
            var transportTransaction = new TransportTransaction();

            //these resources are meant to be used by anyone except message dispatcher e.g. persister
            transportTransaction.Set(SettingsKeys.TransportTransactionSqlConnectionKey, connection);
            transportTransaction.Set(SettingsKeys.TransportTransactionSqlTransactionKey, transaction);

            if (transactionForReceiveOnly)
            {
                //this indicates to MessageDispatcher that it should not reuse connection or transaction for sends
                transportTransaction.Set(ReceiveOnlyTransactionMode, true);
            }

            return(transportTransaction);
        }
Exemple #8
0
        /// <summary>
        /// Enables the use of custom SqlConnection for publish operations.
        /// </summary>
        /// <param name="options">The <see cref="PublishOptions" /> to extend.</param>
        /// <param name="connection">SqlConnection instance that will be used by any operations performed by the transport.</param>
        public static void UseCustomSqlConnection(this PublishOptions options, SqlConnection connection)
        {
            if (connection == null)
            {
                throw new ArgumentException(nameof(connection));
            }

            options.RequireImmediateDispatch();

            var transportTransaction = new TransportTransaction();

            transportTransaction.Set(SettingsKeys.IsUserProvidedTransactionKey, true);
            transportTransaction.Set(SettingsKeys.TransportTransactionSqlConnectionKey, connection);

            options.GetExtensions().Set(transportTransaction);
        }
        public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
        {
            Guard.AgainstNull(nameof(outgoingMessages), outgoingMessages);

            if (outgoingMessages.MulticastTransportOperations.Any())
            {
                throw new Exception("The MSMQ transport only supports unicast transport operations.");
            }

            foreach (var unicastTransportOperation in outgoingMessages.UnicastTransportOperations)
            {
                ExecuteTransportOperation(transaction, unicastTransportOperation);
            }

            return TaskEx.CompletedTask;
        }
        async Task DispatchDefault(SortingResult sortedOperations, TransportTransaction transportTransaction)
        {
            if (sortedOperations.DefaultDispatch == null)
            {
                return;
            }

            if (InReceiveWithNoTransactionMode(transportTransaction) || InReceiveOnlyTransportTransactionMode(transportTransaction))
            {
                await DispatchUsingNewConnectionAndTransaction(sortedOperations.DefaultDispatch).ConfigureAwait(false);

                return;
            }

            await DispatchUsingReceiveTransaction(transportTransaction, sortedOperations.DefaultDispatch).ConfigureAwait(false);
        }
        public override async Task ProcessMessage(CancellationTokenSource stopBatchCancellationTokenSource, CancellationToken cancellationToken = default)
        {
            using (var connection = await connectionFactory.OpenNewConnection(cancellationToken).ConfigureAwait(false))
            {
                MessageReadResult receiveResult;
                using (var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted))
                {
                    receiveResult = await InputQueue.TryReceive(connection, transaction, cancellationToken).ConfigureAwait(false);

                    if (receiveResult == MessageReadResult.NoMessage)
                    {
                        stopBatchCancellationTokenSource.Cancel();
                        return;
                    }

                    if (receiveResult.IsPoison)
                    {
                        await ErrorQueue.DeadLetter(receiveResult.PoisonMessage, connection, transaction, cancellationToken).ConfigureAwait(false);

                        transaction.Commit();
                        return;
                    }

                    if (await TryHandleDelayedMessage(receiveResult.Message, connection, transaction, cancellationToken).ConfigureAwait(false))
                    {
                        transaction.Commit();
                        return;
                    }

                    transaction.Commit();
                }

                var context = new ContextBag();
                var transportTransaction = new TransportTransaction();
                transportTransaction.Set(SettingsKeys.TransportTransactionSqlConnectionKey, connection);

                try
                {
                    await TryHandleMessage(receiveResult.Message, transportTransaction, context, cancellationToken).ConfigureAwait(false);
                }
                catch (Exception ex) when(!ex.IsCausedBy(cancellationToken))
                {
                    // Since this is TransactionMode.None, we don't care whether error handling says handled or retry. Message is gone either way.
                    _ = await HandleError(ex, receiveResult.Message, transportTransaction, 1, context, cancellationToken).ConfigureAwait(false);
                }
            }
        }
        static ErrorContext ToErrorContext(ResolvedEvent evnt, Exception ex, TransportTransaction transportTransaction, int failures)
        {
            var metadata = evnt.Event.Metadata.ParseJson <MessageMetadata>();
            var headers  = new Dictionary <string, string>(metadata.Headers);
            var data     = metadata.Empty //because EventStore inserts {}
                ? new byte[0]
                : evnt.Event.Data;
            string contentType;

            if (headers.TryGetValue(Headers.ContentType, out contentType))
            {
                data = contentType != ContentTypes.Json
                    ? Convert.FromBase64String(Encoding.UTF8.GetString(data))
                    : data;
            }
            return(new ErrorContext(ex, headers, metadata.MessageId, data, transportTransaction, failures));
        }
        public Task Dispatch(TransportOperations operations, TransportTransaction transportTransaction, ContextBag context)
        {
            var outgoingBatches = batcher.ToBatches(operations);

            if (!TryGetReceiveContext(transportTransaction, out var receiveContext)) // not in a receive context, so send out immediately
            {
                return(routeOutgoingBatches.RouteBatches(outgoingBatches, null, DispatchConsistency.Default));
            }

            if (receiveContext is BrokeredMessageReceiveContextInternal brokeredMessageReceiveContext) // apply brokered message specific dispatching rules
            {
                return(DispatchBatches(outgoingBatches, brokeredMessageReceiveContext));
            }
            // case when the receive context is different from brokered messaging (like eventhub)

            return(routeOutgoingBatches.RouteBatches(outgoingBatches, receiveContext, DispatchConsistency.Default)); // otherwise send out immediately
        }
Exemple #14
0
        public async Task Should_upload_large_isolated_operations_request_to_s3()
        {
            var settings            = new SettingsHolder();
            var transportExtensions = new TransportExtensions <SqsTransport>(settings);

            transportExtensions.S3("someBucket", "somePrefix");

            var mockS3Client  = new MockS3Client();
            var mockSqsClient = new MockSqsClient();

            var transportConfiguration = new TransportConfiguration(settings);
            var dispatcher             = new MessageDispatcher(transportConfiguration, mockS3Client, mockSqsClient, null, new QueueCache(mockSqsClient, transportConfiguration), null);

            var transportOperations = new TransportOperations(
                new TransportOperation(
                    new OutgoingMessage(Guid.NewGuid().ToString(), new Dictionary <string, string>(), Encoding.Default.GetBytes(new string('x', 256 * 1024))),
                    new UnicastAddressTag("address1"),
                    DispatchConsistency.Isolated),
                new TransportOperation(
                    new OutgoingMessage(Guid.NewGuid().ToString(), new Dictionary <string, string>(), Encoding.Default.GetBytes(new string('x', 256 * 1024))),
                    new UnicastAddressTag("address2"),
                    DispatchConsistency.Isolated),
                new TransportOperation( /* Crazy long message id will cause the message to go over limits because attributes count as well */
                    new OutgoingMessage(new string('x', 256 * 1024), new Dictionary <string, string>(), Encoding.Default.GetBytes("{}")),
                    new UnicastAddressTag("address2"),
                    DispatchConsistency.Isolated));

            var transportTransaction = new TransportTransaction();
            var context = new ContextBag();

            await dispatcher.Dispatch(transportOperations, transportTransaction, context);

            Assert.AreEqual(3, mockSqsClient.RequestsSent.Count);
            Assert.AreEqual(3, mockS3Client.PutObjectRequestsSent.Count);

            var firstUpload  = mockS3Client.PutObjectRequestsSent.ElementAt(0);
            var secondUpload = mockS3Client.PutObjectRequestsSent.ElementAt(1);
            var thirdUpload  = mockS3Client.PutObjectRequestsSent.ElementAt(2);

            Assert.AreEqual("someBucket", firstUpload.BucketName);
            Assert.AreEqual("someBucket", secondUpload.BucketName);
            Assert.AreEqual("someBucket", thirdUpload.BucketName);
            StringAssert.Contains($@"""Body"":"""",""S3BodyKey"":""{firstUpload.Key}", mockSqsClient.RequestsSent.ElementAt(0).MessageBody);
            StringAssert.Contains($@"""Body"":"""",""S3BodyKey"":""{secondUpload.Key}", mockSqsClient.RequestsSent.ElementAt(1).MessageBody);
            StringAssert.Contains($@"""Body"":"""",""S3BodyKey"":""{thirdUpload.Key}", mockSqsClient.RequestsSent.ElementAt(2).MessageBody);
        }
Exemple #15
0
    public async Task Adapts_transport_connection()
    {
        var transportTransaction = new TransportTransaction();

        var transportConnection = connectionManager.BuildNonContextual();
        var transaction         = transportConnection.BeginTransaction();

        transportTransaction.Set("System.Data.SqlClient.SqlConnection", transportConnection);
        transportTransaction.Set("System.Data.SqlClient.SqlTransaction", transaction);

        var altConnectionManager = new ConnectionManager(() => throw new Exception("Should not be called"));

        var result = await sqlDialect.Convert().TryAdaptTransportConnection(transportTransaction, new ContextBag(), altConnectionManager,
                                                                            (conn, tx, arg3) => new StorageSession(conn, tx, false, null));

        Assert.IsNotNull(result);
    }
Exemple #16
0
            internal override async Task <StorageSession> TryAdaptTransportConnection(
                TransportTransaction transportTransaction,
                ContextBag context,
                IConnectionManager connectionManager,
                Func <DbConnection, DbTransaction, bool, StorageSession> storageSessionFactory,
                CancellationToken cancellationToken = default)
            {
                if (DoNotUseTransportConnection)
                {
                    return(null);
                }

                // SQL server transport in native TX mode
                if (transportTransaction.TryGet("System.Data.SqlClient.SqlConnection", out DbConnection existingSqlConnection) &&
                    transportTransaction.TryGet("System.Data.SqlClient.SqlTransaction", out DbTransaction existingSqlTransaction))
                {
                    return(storageSessionFactory(existingSqlConnection, existingSqlTransaction, false));
                }

                // Transport supports DTC and uses TxScope owned by the transport
                var scopeTx = Transaction.Current;

                if (transportTransaction.TryGet(out Transaction transportTx) &&
                    scopeTx != null &&
                    transportTx != scopeTx)
                {
                    throw new Exception("A TransactionScope has been opened in the current context overriding the one created by the transport. "
                                        + "This setup can result in inconsistent data because operations done via connections enlisted in the context scope won't be committed "
                                        + "atomically with the receive transaction. To manually control the TransactionScope in the pipeline switch the transport transaction mode "
                                        + $"to values lower than '{nameof(TransportTransactionMode.TransactionScope)}'.");
                }

                var ambientTransaction = transportTx ?? scopeTx;

                if (ambientTransaction == null)
                {
                    // Other modes handled by creating a new session.
                    return(null);
                }

                var connection = await connectionManager.OpenConnection(context.GetIncomingMessage(), cancellationToken).ConfigureAwait(false);

                connection.EnlistTransaction(ambientTransaction);

                return(storageSessionFactory(connection, null, true));
            }
        public async Task MoveToErrorQueue_should_dispatch_message_to_error_queue()
        {
            var customErrorQueue     = "random_error_queue";
            var transportTransaction = new TransportTransaction();
            var incomingMessage      = new IncomingMessage("messageId", new Dictionary <string, string>(), new byte[0]);

            await moveToErrorsExecutor.MoveToErrorQueue(customErrorQueue, incomingMessage, new Exception(), transportTransaction);

            Assert.That(dispatcher.TransportOperations.MulticastTransportOperations.Count(), Is.EqualTo(0));
            Assert.That(dispatcher.TransportOperations.UnicastTransportOperations.Count(), Is.EqualTo(1));
            Assert.That(dispatcher.Transaction, Is.EqualTo(transportTransaction));

            var outgoingMessage = dispatcher.TransportOperations.UnicastTransportOperations.Single();

            Assert.That(outgoingMessage.Destination, Is.EqualTo(customErrorQueue));
            Assert.That(outgoingMessage.Message.MessageId, Is.EqualTo(incomingMessage.MessageId));
        }
        Task SendReadyMessage(int capacity, bool isStarting, TransportTransaction transaction)
        {
            //we use the actual address to make sure that the worker inside the master node will check in correctly
            var readyMessage = ControlMessageFactory.Create(MessageIntentEnum.Send);

            readyMessage.Headers.Add(LegacyDistributorHeaders.WorkerCapacityAvailable, capacity.ToString());
            readyMessage.Headers.Add(LegacyDistributorHeaders.WorkerSessionId, workerSessionId);
            readyMessage.Headers.Add(Headers.ReplyToAddress, receiveAddress);

            if (isStarting)
            {
                readyMessage.Headers.Add(LegacyDistributorHeaders.WorkerStarting, bool.TrueString);
            }

            var transportOperation = new TransportOperation(readyMessage, new UnicastAddressTag(distributorControlAddress));
            return dispatcher.Dispatch(new TransportOperations(transportOperation), transaction, new ContextBag());
        }
Exemple #19
0
        public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction,
                             CancellationToken cancellationToken = default)
        {
            Guard.AgainstNull(nameof(outgoingMessages), outgoingMessages);

            if (outgoingMessages.MulticastTransportOperations.Any())
            {
                throw new Exception("The MSMQ transport only supports unicast transport operations.");
            }

            foreach (var unicastTransportOperation in outgoingMessages.UnicastTransportOperations)
            {
                ExecuteTransportOperation(transaction, unicastTransportOperation);
            }

            return(Task.CompletedTask);
        }
Exemple #20
0
        public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
        {
            var allIds = outgoingMessages.MulticastTransportOperations.Select(mto => mto.Message.MessageId).ToList();

            allIds.AddRange(outgoingMessages.UnicastTransportOperations.Select(mto => mto.Message.MessageId));
            var alreadyFailedOnce = allIds.All(id => _idsForWhichDispatchAlreadyFailedOnce.Contains(id));

            if (_failDispatchOnce && !alreadyFailedOnce)
            {
                _idsForWhichDispatchAlreadyFailedOnce.AddRange(allIds);
                throw new Exception("Bad!");
            }

            return(Task.WhenAll(
                       DispatchUnicast(outgoingMessages.UnicastTransportOperations, transaction),
                       DispatchMulticast(outgoingMessages.MulticastTransportOperations, transaction)));
        }
        async Task TrySendDelayedMessageToErrorQueue(DelayedMessage timeout, Exception exception, CancellationToken cancellationToken)
        {
            try
            {
                bool success = await delayedMessageStore.Remove(timeout, cancellationToken).ConfigureAwait(false);

                if (!success)
                {
                    // Already dispatched
                    return;
                }

                Dictionary <string, string> headersAndProperties = MsmqUtilities.DeserializeMessageHeaders(timeout.Headers);

                ExceptionHeaderHelper.SetExceptionHeaders(headersAndProperties, exception);
                headersAndProperties[FaultsHeaderKeys.FailedQ] = timeoutsQueueTransportAddress;
                foreach (KeyValuePair <string, string> pair in faultMetadata)
                {
                    headersAndProperties[pair.Key] = pair.Value;
                }

                Log.InfoFormat("Move {0} to error queue", timeout.MessageId);
                using (var transportTx = new TransactionScope(txOption, transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
                {
                    var transportTransaction = new TransportTransaction();
                    transportTransaction.Set(Transaction.Current);

                    var outgoingMessage    = new OutgoingMessage(timeout.MessageId, headersAndProperties, timeout.Body);
                    var transportOperation = new TransportOperation(outgoingMessage, new UnicastAddressTag(errorQueue));
                    await dispatcher.Dispatch(new TransportOperations(transportOperation), transportTransaction, CancellationToken.None)
                    .ConfigureAwait(false);

                    transportTx.Complete();
                }
            }
            catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested)
            {
                //Shutting down
                Log.Debug("Aborted sending delayed message to error queue due to shutdown.");
            }
            catch (Exception ex)
            {
                Log.Error($"Failed to move delayed message {timeout.MessageId} to the error queue {errorQueue} after {timeout.NumberOfRetries} failed attempts at dispatching it to the destination", ex);
            }
        }
        Task SendReadyMessage(int capacity, bool isStarting, TransportTransaction transaction)
        {
            //we use the actual address to make sure that the worker inside the master node will check in correctly
            var readyMessage = ControlMessageFactory.Create(MessageIntentEnum.Send);

            readyMessage.Headers.Add(LegacyDistributorHeaders.WorkerCapacityAvailable, capacity.ToString());
            readyMessage.Headers.Add(LegacyDistributorHeaders.WorkerSessionId, workerSessionId);
            readyMessage.Headers.Add(Headers.ReplyToAddress, receiveAddress);

            if (isStarting)
            {
                readyMessage.Headers.Add(LegacyDistributorHeaders.WorkerStarting, bool.TrueString);
            }

            var transportOperation = new TransportOperation(readyMessage, new UnicastAddressTag(distributorControlAddress));

            return(dispatcher.Dispatch(new TransportOperations(transportOperation), transaction, new ContextBag()));
        }
Exemple #23
0
        public Task <CompletableSynchronizedStorageSession> TryAdapt(TransportTransaction transportTransaction, ContextBag context)
        {
            Transaction ambientTransaction;

            if (!transportTransaction.TryGet(out ambientTransaction))
            {
                return(EmptyResult);
            }
            var sessionFactoryImpl = sessionFactory as SessionFactoryImpl;

            if (sessionFactoryImpl == null)
            {
                throw new NotSupportedException("Overriding default implementation of ISessionFactory is not supported.");
            }
            CompletableSynchronizedStorageSession session = new NHibernateLazyAmbientTransactionSynchronizedStorageSession(() => OpenConnection(sessionFactoryImpl, ambientTransaction), conn => sessionFactory.OpenSession(conn));

            return(Task.FromResult(session));
        }
        async Task DispatchUsingReceiveTransaction(TransportTransaction transportTransaction, List <UnicastTransportOperation> operations)
        {
            transportTransaction.TryGet(out SqlConnection sqlTransportConnection);
            transportTransaction.TryGet(out SqlTransaction sqlTransportTransaction);
            transportTransaction.TryGet(out Transaction ambientTransaction);

            if (ambientTransaction != null)
            {
                using (var connection = await connectionFactory.OpenNewConnection().ConfigureAwait(false))
                {
                    await Send(operations, connection, null).ConfigureAwait(false);
                }
            }
            else
            {
                await Send(operations, sqlTransportConnection, sqlTransportTransaction).ConfigureAwait(false);
            }
        }
 public async Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
 {
     try
     {
         var operations = outgoingMessages.UnicastTransportOperations;
         var tasks      = new Task[operations.Count];
         for (var i = 0; i < operations.Count; i++)
         {
             tasks[i] = Dispatch(operations[i]);
         }
         await Task.WhenAll(tasks).ConfigureAwait(false);
     }
     catch (Exception e)
     {
         Logger.Error("Exception from Send.", e);
         throw;
     }
 }
Exemple #26
0
        public async Task SendToError(Exception ex, IFullMessage message)
        {
            var transportTransaction = new TransportTransaction();

            var messageBytes = _serializer.Serialize(message.Message);

            var headers = new Dictionary <string, string>(message.Headers);

            var errorContext = new ErrorContext(ex, headers,
                                                Guid.NewGuid().ToString(),
                                                messageBytes, transportTransaction,
                                                int.MaxValue);

            if ((await Bus.OnError(errorContext).ConfigureAwait(false)) != ErrorHandleResult.Handled)
            {
                throw new InvalidOperationException("Failed to send message error queue");
            }
        }
Exemple #27
0
        protected async Task <bool> TryProcessingMessage(Message message, TransportTransaction transportTransaction)
        {
            if (message.Expired) //Do not process expired messages
            {
                return(true);
            }
            using (var pushCancellationTokenSource = new CancellationTokenSource())
            {
                var messageContext = new MessageContext(message.TransportId, message.Headers, message.Body, transportTransaction, pushCancellationTokenSource, new ContextBag());
                await onMessage(messageContext).ConfigureAwait(false);

                // Cancellation is requested when message processing is aborted.
                // We return the opposite value:
                //  - true when message processing completed successfully,
                //  - false when message processing was aborted.
                return(!pushCancellationTokenSource.Token.IsCancellationRequested);
            }
        }
Exemple #28
0
        public Task Init(Func <MessageContext, Task> pump, Func <ErrorContext, Task <ErrorHandleResult> > onError, CriticalError criticalError, PushSettings pushSettings)
        {
            topologyOperator = DetermineTopologyOperator(pushSettings.InputQueue);

            messagePump = pump;
            var name = $"MessagePump on the queue `{pushSettings.InputQueue}`";

            circuitBreaker = new RepeatedFailuresOverTimeCircuitBreaker(name, timeToWaitBeforeTriggering, ex => criticalError.Raise("Failed to receive message from Azure Service Bus.", ex));

            if (pushSettings.PurgeOnStartup)
            {
                throw new InvalidOperationException("Azure Service Bus transport doesn't support PurgeOnStartup behavior");
            }

            inputQueue = pushSettings.InputQueue;


            topologyOperator.OnIncomingMessage(async(incoming, receiveContext) =>
            {
                var tokenSource = new CancellationTokenSource();
                receiveContext.CancellationToken = tokenSource.Token;

                circuitBreaker.Success();

                var transportTransaction = new TransportTransaction();
                transportTransaction.Set(receiveContext);

                await throttler.WaitAsync(receiveContext.CancellationToken).ConfigureAwait(false);

                try
                {
                    await messagePump(new MessageContext(incoming.MessageId, incoming.Headers, incoming.Body, transportTransaction, tokenSource, new ContextBag())).ConfigureAwait(false);
                }
                finally
                {
                    throttler.Release();
                }
            });

            topologyOperator.OnError(exception => circuitBreaker.Failure(exception));
            topologyOperator.OnProcessingFailure(onError);

            return(TaskEx.Completed);
        }
Exemple #29
0
        public async Task Should_deduplicate_if_compatibility_mode_is_enabled_and_subscription_found()
        {
            var settings = new SettingsHolder();

            var mockSnsClient = new MockSnsClient();
            var mockSqsClient = new MockSqsClient();

            var transportConfiguration = new TransportConfiguration(settings);
            var dispatcher             = new MessageDispatcher(transportConfiguration, null, mockSqsClient, mockSnsClient, new QueueCache(mockSqsClient, transportConfiguration), new TopicCache(mockSnsClient, settings.SetupMessageMetadataRegistry(), transportConfiguration));

            mockSnsClient.ListSubscriptionsByTopicResponse = topic => new ListSubscriptionsByTopicResponse
            {
                Subscriptions = new List <Subscription>
                {
                    new Subscription {
                        Endpoint = "arn:abc", SubscriptionArn = "arn:subscription"
                    }
                }
            };

            var messageId = Guid.NewGuid().ToString();
            var headers   = new Dictionary <string, string>()
            {
                { Headers.EnclosedMessageTypes, typeof(Event).AssemblyQualifiedName },
                { Headers.MessageIntent, MessageIntentEnum.Publish.ToString() }
            };
            var transportOperations = new TransportOperations(
                new TransportOperation(
                    new OutgoingMessage(messageId, headers, Encoding.Default.GetBytes("{}")),
                    new MulticastAddressTag(typeof(Event))),
                new TransportOperation(
                    new OutgoingMessage(messageId, headers, Encoding.Default.GetBytes("{}")),
                    new UnicastAddressTag("abc"))
                );

            var transportTransaction = new TransportTransaction();
            var context = new ContextBag();

            await dispatcher.Dispatch(transportOperations, transportTransaction, context);

            Assert.AreEqual(1, mockSnsClient.PublishedEvents.Count);
            Assert.IsEmpty(mockSqsClient.RequestsSent);
            Assert.IsEmpty(mockSqsClient.BatchRequestsSent);
        }
        public async Task <CompletableSynchronizedStorageSession> TryAdapt(TransportTransaction transportTransaction, ContextBag context)
        {
            Transaction ambientTransaction;

            if (transportTransaction.TryGet(out ambientTransaction))
            {
                NpgsqlConnection existingSqlConnection;
                //SQL server transport in ambient TX mode
                if (transportTransaction.TryGet(out existingSqlConnection))
                {
                    return(new StorageSession(existingSqlConnection, null));
                }
                //Other transport in ambient TX mode
                var connection = await NpgSqlHelpers.NewConnection(_connectionString);

                return(new StorageSession(connection, connection.BeginTransaction()));
            }
            return(null);
        }
        public async Task Save_should_fail_when_data_changes_between_concurrent_instances()
        {
            configuration.RequiresDtcSupport();

            var persister = configuration.SagaStorage;
            var sagaData  = new TestSagaData {
                SomeId = Guid.NewGuid().ToString()
            };

            await SaveSaga(sagaData);

            var generatedSagaId = sagaData.Id;

            Assert.That(async() =>
            {
                var storageAdapter = configuration.SynchronizedStorageAdapter;
                using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    Transaction.Current.EnlistDurable(EnlistmentWhichEnforcesDtcEscalation.Id, new EnlistmentWhichEnforcesDtcEscalation(), EnlistmentOptions.None);

                    var transportTransaction = new TransportTransaction();
                    transportTransaction.Set(Transaction.Current);

                    var unenlistedContextBag = configuration.GetContextBagForSagaStorage();
                    using (var unenlistedSession = await configuration.SynchronizedStorage.OpenSession(unenlistedContextBag))
                    {
                        var enlistedContextBag = configuration.GetContextBagForSagaStorage();
                        var enlistedSession    = await storageAdapter.TryAdapt(transportTransaction, enlistedContextBag);

                        var unenlistedRecord = await persister.Get <TestSagaData>(generatedSagaId, unenlistedSession, unenlistedContextBag);

                        var enlistedRecord = await persister.Get <TestSagaData>(generatedSagaId, enlistedSession, enlistedContextBag);

                        await persister.Update(unenlistedRecord, unenlistedSession, unenlistedContextBag);
                        await persister.Update(enlistedRecord, enlistedSession, enlistedContextBag);

                        await unenlistedSession.CompleteAsync();
                    }

                    tx.Complete();
                }
            }, Throws.Exception.TypeOf <TransactionAbortedException>());
        }
Exemple #32
0
        async Task <bool> ProcessMessage(MessageQueueTransaction msmqTransaction, Message message, Dictionary <string, string> headers)
        {
            var transportTransaction = new TransportTransaction();

            transportTransaction.Set(msmqTransaction);

            MsmqFailureInfoStorage.ProcessingFailureInfo failureInfo;

            var shouldTryProcessMessage = true;

            if (failureInfoStorage.TryGetFailureInfoForMessage(message.Id, out failureInfo))
            {
                var errorHandleResult = await HandleError(message, headers, failureInfo.Exception, transportTransaction, failureInfo.NumberOfProcessingAttempts).ConfigureAwait(false);

                shouldTryProcessMessage = errorHandleResult != ErrorHandleResult.Handled;
            }

            if (shouldTryProcessMessage)
            {
                try
                {
                    using (var bodyStream = message.BodyStream)
                    {
                        var shouldAbortMessageProcessing = await TryProcessMessage(message, headers, bodyStream, transportTransaction).ConfigureAwait(false);

                        if (shouldAbortMessageProcessing)
                        {
                            return(false);
                        }
                    }
                }
                catch (Exception exception)
                {
                    failureInfoStorage.RecordFailureInfoForMessage(message.Id, exception);

                    return(false);
                }
            }

            failureInfoStorage.ClearFailureInfoForMessage(message.Id);

            return(true);
        }
        public Task MessageProcessed(Dictionary<string, string> headers, TransportTransaction transaction)
        {
            //if there was a failure this "send" will be rolled back
            string messageSessionId;
            headers.TryGetValue(LegacyDistributorHeaders.WorkerSessionId, out messageSessionId);
            if (messageSessionId == null)
            {
                return TaskEx.CompletedTask;
            }
            var messageId = headers[Headers.MessageId];
            Logger.DebugFormat("Got message with id {0} and messageSessionId {1}. WorkerSessionId is {2}", messageId, messageSessionId, workerSessionId);
            if (messageSessionId != workerSessionId)
            {
                Logger.InfoFormat("SKIPPED Ready message for message with id {0} because of sessionId mismatch. MessageSessionId {1}, WorkerSessionId {2}", messageId, messageSessionId, workerSessionId);
                return TaskEx.CompletedTask;
            }

            return SendReadyMessage(1, false, transaction);
        }
        async Task<bool> ProcessMessage(MessageQueueTransaction msmqTransaction, Message message, Dictionary<string, string> headers)
        {
            var transportTransaction = new TransportTransaction();

            transportTransaction.Set(msmqTransaction);

            MsmqFailureInfoStorage.ProcessingFailureInfo failureInfo;

            var shouldTryProcessMessage = true;

            if (failureInfoStorage.TryGetFailureInfoForMessage(message.Id, out failureInfo))
            {
                var errorHandleResult = await HandleError(message, headers, failureInfo.Exception, transportTransaction, failureInfo.NumberOfProcessingAttempts).ConfigureAwait(false);

                shouldTryProcessMessage = errorHandleResult != ErrorHandleResult.Handled;
            }

            if (shouldTryProcessMessage)
            {
                try
                {
                    using (var bodyStream = message.BodyStream)
                    {
                        var shouldAbortMessageProcessing = await TryProcessMessage(message.Id, headers, bodyStream, transportTransaction).ConfigureAwait(false);

                        if (shouldAbortMessageProcessing)
                        {
                            return false;
                        }
                    }
                }
                catch (Exception exception)
                {
                    failureInfoStorage.RecordFailureInfoForMessage(message.Id, exception);

                    return false;
                }
            }

            failureInfoStorage.ClearFailureInfoForMessage(message.Id);

            return true;
        }
    public void It_throws_if_non_transport_transaction_scope_exists()
    {
        if (!SupportsDistributedTransactions)
        {
            Assert.Ignore();
        }
        using (new TransactionScope())
        {
            var transportTransaction = new TransportTransaction();
            transportTransaction.Set(Transaction.Current);
            using (new TransactionScope(TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled))
            {
                var ex = Assert.ThrowsAsync <Exception>(() => sqlDialect.Convert().TryAdaptTransportConnection(transportTransaction, new ContextBag(), dbConnection,
                                                                                                               (conn, tx, arg3) => new StorageSession(conn, tx, false, null)));

                StringAssert.StartsWith("A TransactionScope has been opened in the current context overriding the one created by the transport.", ex.Message);
            }
        }
    }
        async Task <bool> ProcessMessage(MessageQueueTransaction msmqTransaction, Message message, Dictionary <string, string> headers, ContextBag context, CancellationToken cancellationToken)
        {
            var transportTransaction = new TransportTransaction();

            transportTransaction.Set(msmqTransaction);

            var length = (int)message.BodyStream.Length;
            var buffer = ArrayPool <byte> .Shared.Rent(length);

            try
            {
                _ = await message.BodyStream.ReadAsync(buffer, 0, length, cancellationToken).ConfigureAwait(false);

                var body = buffer.AsMemory(0, length);

                if (failureInfoStorage.TryGetFailureInfoForMessage(message.Id, out var failureInfo))
                {
                    var errorHandleResult = await HandleError(message, body, failureInfo.Exception, transportTransaction, failureInfo.NumberOfProcessingAttempts, failureInfo.Context, cancellationToken).ConfigureAwait(false);

                    if (errorHandleResult == ErrorHandleResult.Handled)
                    {
                        return(true);
                    }
                }

                try
                {
                    await TryProcessMessage(message.Id, headers, body, transportTransaction, context, cancellationToken).ConfigureAwait(false);

                    return(true);
                }
                catch (Exception ex) when(!ex.IsCausedBy(cancellationToken))
                {
                    failureInfoStorage.RecordFailureInfoForMessage(message.Id, ex, context);

                    return(false);
                }
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(buffer);
            }
        }
Exemple #37
0
        public async Task Save_fails_when_data_changes_between_concurrent_instances()
        {
            var saga = new TestSagaData
            {
                Id = Guid.NewGuid()
            };

            var persister      = new InMemorySagaPersister();
            var storageAdapter = new InMemoryTransactionalSynchronizedStorageAdapter();
            var insertSession  = new InMemorySynchronizedStorageSession();

            await persister.Save(saga, SagaMetadataHelper.GetMetadata <TestSaga>(saga), insertSession, new ContextBag());

            await insertSession.CompleteAsync();

            Assert.That(async() =>
            {
                using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    Transaction.Current.EnlistDurable(EnlistmentWhichEnforcesDtcEscalation.Id, new EnlistmentWhichEnforcesDtcEscalation(), EnlistmentOptions.None);

                    var transportTransaction = new TransportTransaction();
                    transportTransaction.Set(Transaction.Current);

                    var unenlistedSession = new InMemorySynchronizedStorageSession();

                    var enlistedSession = await storageAdapter.TryAdapt(transportTransaction, new ContextBag());

                    var unenlistedSessionContext = new ContextBag();
                    var unenlistedRecord         = await persister.Get <TestSagaData>(saga.Id, unenlistedSession, unenlistedSessionContext);
                    var enlistedSessionContext   = new ContextBag();
                    var enlistedRecord           = await persister.Get <TestSagaData>("Id", saga.Id, enlistedSession, enlistedSessionContext);

                    await persister.Update(unenlistedRecord, unenlistedSession, unenlistedSessionContext);
                    await persister.Update(enlistedRecord, enlistedSession, enlistedSessionContext);

                    await unenlistedSession.CompleteAsync();

                    tx.Complete();
                }
            }, Throws.Exception.TypeOf <TransactionAbortedException>());
        }
Exemple #38
0
        Task DispatchUnicast(IEnumerable <UnicastTransportOperation> operations, TransportTransaction transaction)
        {
            return(Task.WhenAll(operations.Select(operation =>
            {
                PathChecker.ThrowForBadPath(operation.Destination, "message destination");

                if (!operation.Message.Headers.TryGetValue(Headers.MessageId, out var messageId))
                {
                    messageId = operation.Message.MessageId;
                }

                if (!operation.Message.Headers.TryGetValue(Headers.MessageIntent, out var intent))
                {
                    intent = "<Unknown>";
                }
                Console.WriteLine($"Sending message {messageId} with intent {intent} sent to {operation.Destination}");

                return WriteMessage(operation.Destination, operation, transaction);
            })));
        }
Exemple #39
0
    public async Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
    {
        var unicastTransportOperations   = outgoingMessages.UnicastTransportOperations;
        var multicastTransportOperations = outgoingMessages.MulticastTransportOperations;

        var tasks = new List <Task>(unicastTransportOperations.Count + multicastTransportOperations.Count);

        foreach (var operation in unicastTransportOperations)
        {
            tasks.Add(SendMessage(operation));
        }

        foreach (var operation in multicastTransportOperations)
        {
            tasks.Add(SendMessage(operation));
        }

        await(tasks.Count == 1 ? tasks[0] : Task.WhenAll(tasks))
        .ConfigureAwait(false);
    }
Exemple #40
0
        public async Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, CancellationToken cancellationToken = default)
        {
            int totalNumberOfOperations = outgoingMessages.UnicastTransportOperations.Count + outgoingMessages.MulticastTransportOperations.Count;

            var unicastOperations = new HashSet<UnicastTransportOperation>(totalNumberOfOperations, MessageIdAndDestinationEqualityComparer.Instance);
            unicastOperations.AddRange(outgoingMessages.UnicastTransportOperations);

            foreach (var multicastTransportOperation in outgoingMessages.MulticastTransportOperations)
            {
                unicastOperations.AddRange(await ConvertTo(multicastTransportOperation, cancellationToken).ConfigureAwait(false));
            }

            var sends = new List<Task>(totalNumberOfOperations);
            foreach (var unicastTransportOperation in unicastOperations)
            {
                sends.Add(Send(unicastTransportOperation, cancellationToken));
            }

            await Task.WhenAll(sends).ConfigureAwait(false);
        }
        public async Task Save_fails_when_data_changes_between_concurrent_instances()
        {
            var saga = new TestSagaData
            {
                Id = Guid.NewGuid()
            };

            var persister = new InMemorySagaPersister();
            var storageAdapter = new InMemoryTransactionalSynchronizedStorageAdapter();
            var insertSession = new InMemorySynchronizedStorageSession();

            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            Assert.That(async () =>
            {
                using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    Transaction.Current.EnlistDurable(EnlistmentWhichEnforcesDtcEscalation.Id, new EnlistmentWhichEnforcesDtcEscalation(), EnlistmentOptions.None);

                    var transportTransaction = new TransportTransaction();
                    transportTransaction.Set(Transaction.Current);

                    var unenlistedSession = new InMemorySynchronizedStorageSession();

                    var enlistedSession = await storageAdapter.TryAdapt(transportTransaction, new ContextBag());

                    var unenlistedSessionContext = new ContextBag();
                    var unenlistedRecord = await persister.Get<TestSagaData>(saga.Id, unenlistedSession, unenlistedSessionContext);
                    var enlistedSessionContext = new ContextBag();
                    var enlistedRecord = await persister.Get<TestSagaData>("Id", saga.Id, enlistedSession, enlistedSessionContext);

                    await persister.Update(unenlistedRecord, unenlistedSession, unenlistedSessionContext);
                    await persister.Update(enlistedRecord, enlistedSession, enlistedSessionContext);

                    await unenlistedSession.CompleteAsync();

                    tx.Complete();
                }
            }, Throws.Exception.TypeOf<TransactionAbortedException>());
        }
        public Task MoveToErrorQueue(string errorQueueAddress, IncomingMessage message, Exception exception, TransportTransaction transportTransaction)
        {
            message.RevertToOriginalBodyIfNeeded();

            var outgoingMessage = new OutgoingMessage(message.MessageId, new Dictionary<string, string>(message.Headers), message.Body);

            var headers = outgoingMessage.Headers;
            headers.Remove(Headers.DelayedRetries);
            headers.Remove(Headers.ImmediateRetries);

            ExceptionHeaderHelper.SetExceptionHeaders(headers, exception);

            foreach (var faultMetadata in staticFaultMetadata)
            {
                headers[faultMetadata.Key] = faultMetadata.Value;
            }

            headerCustomizations(headers);

            var transportOperations = new TransportOperations(new TransportOperation(outgoingMessage, new UnicastAddressTag(errorQueueAddress)));

            return dispatcher.Dispatch(transportOperations, transportTransaction, new ContextBag());
        }
 public DispatchedMessage(TransportOperations operations, TransportTransaction transaction, ContextBag context)
 {
     Operations = operations;
     Context = context;
     Transaction = transaction;
 }
 public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transportTransaction, ContextBag context)
 {
     MessagesSent += outgoingMessages.MulticastTransportOperations.Count + outgoingMessages.UnicastTransportOperations.Count;
     return TaskEx.CompletedTask;
 }
 public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
 {
     DispatchedMessages.Add(new DispatchedMessage(outgoingMessages, transaction, context));
     return TaskEx.CompletedTask;
 }
        protected async Task<bool> TryProcessMessage(string messageId, Dictionary<string, string> headers, Stream bodyStream, TransportTransaction transaction)
        {
            using (var tokenSource = new CancellationTokenSource())
            {
                var body = await ReadStream(bodyStream).ConfigureAwait(false);
                var messageContext = new MessageContext(messageId, headers, body, transaction, tokenSource, new ContextBag());

                await onMessage(messageContext).ConfigureAwait(false);

                return tokenSource.Token.IsCancellationRequested;
            }
        }
 public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
 {
     TransportOperations = outgoingMessages;
     ContextBag = context;
     Transaction = transaction;
     return Task.FromResult(0);
 }
        protected async Task<ErrorHandleResult> HandleError(Message message, Dictionary<string, string> headers, Exception exception, TransportTransaction transportTransaction, int processingAttempts)
        {
            try
            {
                var body = await ReadStream(message.BodyStream).ConfigureAwait(false);
                var errorContext = new ErrorContext(exception, headers, message.Id, body, transportTransaction, processingAttempts);

                return await onError(errorContext).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                criticalError.Raise($"Failed to execute recoverability actions for message `{message.Id}`", ex);

                //best thing we can do is roll the message back if possible
                return ErrorHandleResult.RetryRequired;
            }
        }
            public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transaction, ContextBag context)
            {
                if (numberOfTimes.HasValue && FailedNumberOfTimes < numberOfTimes.Value)
                {
                    FailedNumberOfTimes++;
                    throw new QueueNotFoundException();
                }

                DispatchedTransportOperations.Add(outgoingMessages);
                return TaskEx.CompletedTask;
            }
 public Task<CompletableSynchronizedStorageSession> TryAdapt(TransportTransaction transportTransaction, ContextBag context)
 {
     return EmptyResult;
 }
        bool IsCombiningTimeToBeReceivedWithTransactions(TransportTransaction transaction, DispatchConsistency requiredDispatchConsistency, List<DeliveryConstraint> deliveryConstraints)
        {
            if (!settings.UseTransactionalQueues)
            {
                return false;
            }

            if (requiredDispatchConsistency == DispatchConsistency.Isolated)
            {
                return false;
            }

            DiscardIfNotReceivedBefore discardIfNotReceivedBefore;
            var timeToBeReceivedRequested = deliveryConstraints.TryGet(out discardIfNotReceivedBefore) && discardIfNotReceivedBefore.MaxTime < MessageQueue.InfiniteTimeout;

            if (!timeToBeReceivedRequested)
            {
                return false;
            }

            if (Transaction.Current != null)
            {
                return true;
            }

            MessageQueueTransaction activeReceiveTransaction;

            return TryGetNativeTransaction(transaction, out activeReceiveTransaction);
        }
 static bool TryGetNativeTransaction(TransportTransaction transportTransaction, out MessageQueueTransaction transaction)
 {
     return transportTransaction.TryGet(out transaction);
 }
        void ExecuteTransportOperation(TransportTransaction transaction, UnicastTransportOperation transportOperation)
        {
            var message = transportOperation.Message;

            var destination = transportOperation.Destination;
            var destinationAddress = MsmqAddress.Parse(destination);

            if (IsCombiningTimeToBeReceivedWithTransactions(
                transaction,
                transportOperation.RequiredDispatchConsistency,
                transportOperation.DeliveryConstraints))
            {
                throw new Exception($"Failed to send message to address: {destinationAddress.Queue}@{destinationAddress.Machine}. Sending messages with a custom TimeToBeReceived is not supported on transactional MSMQ.");
            }

            try
            {
                using (var q = new MessageQueue(destinationAddress.FullPath, false, settings.UseConnectionCache, QueueAccessMode.Send))
                {
                    using (var toSend = MsmqUtilities.Convert(message, transportOperation.DeliveryConstraints))
                    {
                        toSend.UseDeadLetterQueue = settings.UseDeadLetterQueue;
                        toSend.UseJournalQueue = settings.UseJournalQueue;
                        toSend.TimeToReachQueue = settings.TimeToReachQueue;

                        string replyToAddress;

                        if (message.Headers.TryGetValue(Headers.ReplyToAddress, out replyToAddress))
                        {
                            toSend.ResponseQueue = new MessageQueue(MsmqAddress.Parse(replyToAddress).FullPath);
                        }

                        var label = GetLabel(message);

                        if (transportOperation.RequiredDispatchConsistency == DispatchConsistency.Isolated)
                        {
                            q.Send(toSend, label, GetIsolatedTransactionType());
                            return;
                        }

                        MessageQueueTransaction activeTransaction;
                        if (TryGetNativeTransaction(transaction, out activeTransaction))
                        {
                            q.Send(toSend, label, activeTransaction);
                            return;
                        }

                        q.Send(toSend, label, GetTransactionTypeForSend());
                    }
                }
            }
            catch (MessageQueueException ex)
            {
                if (ex.MessageQueueErrorCode == MessageQueueErrorCode.QueueNotFound)
                {
                    var msg = destination == null
                        ? "Failed to send message. Target address is null."
                        : $"Failed to send message to address: [{destination}]";

                    throw new QueueNotFoundException(destination, msg, ex);
                }

                ThrowFailedToSendException(destination, ex);
            }
            catch (Exception ex)
            {
                ThrowFailedToSendException(destination, ex);
            }
        }
 public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transportTransaction, ContextBag context)
 {
     OutgoingTransportOperations = outgoingMessages;
     TransportTransactionUsed = transportTransaction;
     return TaskEx.CompletedTask;
 }
 public Task Dispatch(TransportOperations outgoingMessages, TransportTransaction transportTransaction, ContextBag context)
 {
     throw new Exception("simulated exception");
 }