The transaction aborted - c#

im creating a C# and WCF service which picks up msgs from MSMQ.
This is using transactional MSMQ. within the business logic, there is a condition and that condition then places a new message on a different transactional queue however I seem to always get an exception thrown and not sure where to go from here
"System.Transactions.TransactionAbortedException: The transaction has aborted.\r\n at System.Transactions.TransactionStatePromotedAborted.CreateAbortingClone(InternalTransaction tx)\r\n at System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking)\r\n at System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption)\r\n at System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent)\r\n at System.Transactions.TransactionScope.PushScope()\r\n at System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption)\r\n at TMC.Services.Implementation.InboundMessageHandler.Msmq.MsmqDispatcher.Dispatch(String queueFormatAndLocation, Object itemToPlaceOnQueue, Boolean traceMessage) in E:\Msmq\MsmqDispatcher.cs:line 39\r\n at TMC.Services.Implementation.InboundMessageHandler.OmhDispatcher.AckNackDispatcher.SendAckTo
Tg(SendAckToTgRequest request) in E:\AckNackDispatcher.cs:line 38"
Any ideas at all?
the code for when it is placing it on the queue:
var queue = new MessageQueue(queueFormatAndLocation);
var msg = new System.Messaging.Message {Body = itemToPlaceOnQueue, Priority = MessagePriority.High, UseDeadLetterQueue = true, UseTracing = traceMessage};
using (var ts = new TransactionScope(TransactionScopeOption.Required))
{
queue.Send(msg, MessageQueueTransactionType.Automatic); // send the message
ts.Complete(); // complete the transaction
}
in terms of the queueFormatAndLocation, it is correct:
"FormatName:Direct=OS:.\private$\AckMsgs"

This helps and seems to work:
http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(%22SYSTEM.MESSAGING.MESSAGEQUEUETRANSACTION.%23CTOR%22);k(SOLUTIONITEMSPROJECT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true
http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(%22SYSTEM.MESSAGING.MESSAGEQUEUE.%23CTOR%22);k(SOLUTIONITEMSPROJECT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true
basically using the MessageQueueTransasction and MessageQueue class. the reason for the MQT is to use it within an existing transaction (in my scenario). this seems to work.
code:
using (var mqt = new MessageQueueTransaction())
{
mqt.Begin();
MessageQueue mq = new MessageQueue(queueFormatAndLocation);
mq.Send(itemToPlaceOnQueue, mqt);
mqt.Commit();
}

Related

How to make Azure Service Bus client to not participate to the ambient transaction when sending a message

I understand that DTC's are not supported by Azure Service Bus and if you try to do so you get an exception like this: 'Local transactions are not supported with other resource managers/DTC.'
My problem is that I need to send a message to a service bus and the code might be executed within a transaction scope together with possible DB-operations. But the service bus doesn't need to be particularly part of this transaction; so, DTC is not really needed here. However, the service bus client seems to participate automatically to the ambient transaction which elevates the transaction to a DTC.
Examples:
This runs correctly (service bus code is the only one in the transaction):
using (var tx = new TransactionScope())
{
//A simple Azure Bus operation
var builder = new ServiceBusConnectionStringBuilder(connectionString);
var queueClient = new QueueClient(builder);
var messageBody = new Message(Encoding.UTF8.GetBytes("Hello"));
messageBody.MessageId = Guid.NewGuid().ToString("N");
queueClient.SendAsync(messageBody).GetAwaiter().GetResult();
tx.Complete();
}
But from the moment another system participates (here an Sql connection) the "DTC are not supported by Azure Service Bus"-exception is thrown:
using (var tx = new TransactionScope())
{
//A simple DB operation
SqlConnection sqlConnection = new SqlConnection(dbConnectionString);
sqlConnection.Open();
SqlCommand cmd = new SqlCommand("INSERT INTO [dbo].[Table_1]([Name]) values ('Hello')", sqlConnection);
cmd.ExecuteNonQuery();
//A simple Azure Bus operation
var builder = new ServiceBusConnectionStringBuilder(connectionString);
var queueClient = new QueueClient(builder);
var messageBody = new Message(Encoding.UTF8.GetBytes("Hello"));
messageBody.MessageId = Guid.NewGuid().ToString("N");
queueClient.SendAsync(messageBody).GetAwaiter().GetResult();
queueClient.CloseAsync().GetAwaiter().GetResult();
sqlConnection.Close();
tx.Complete();
}
This error is understandable and already explained here.
But is there a way to tell the service bus client to ignore the ambient transaction?
You will need to suppress the ambient transaction and wrap your Service Bus code with the following:
public async Task Method()
{
SqlConnection sqlConnection = new SqlConnection(dbConnectionString);
sqlConnection.Open();
using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
SqlCommand cmd = new SqlCommand("INSERT INTO [dbo].[Table_1]([Name]) values
('Hello')", sqlConnection);
await cmd.ExecuteNonQueryAsync();
using (var tx = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
{
var builder = new ServiceBusConnectionStringBuilder(connectionString);
var queueClient = new QueueClient(builder);
var messageBody = new Message(Encoding.UTF8.GetBytes("Hello"));
messageBody.MessageId = Guid.NewGuid().ToString("N");
queueClient.SendAsync(messageBody).GetAwaiter().GetResult();
queueClient.CloseAsync().GetAwaiter().GetResult();
tx.Complete();
}
tx.Complete();
}
sqlConnection.Close();
}
Note that
You should not be recreating your queue client each time. Keep it around for performance considerations.
Do not use asynchronous APIs in a synchronous code. Rather convert your method to be async. SQL operations are IO-bound just as Service Bus. It's better to have the method to be asynchronous.

Sending Queue Message on Artemis using C#

I am actually trying to send a queue message on Artemis using C#.
An instance is being created but not as I would like and when I check the queue message, it says 0. Also I want to add an expiry date and set the Routing_Type to ANYCAST, but cannot see where to specify those details.
Find below my actual code, where I tried setting an expiry date of 5minutes;
private static void SendMsgAtemis()
{
string address = "amqp://tst01sacmamq.corporate.intra:61616";
Connection connection = new Connection(new Address(address));
Session session = new Session(connection);
SenderLink sender = new SenderLink(session, "test-sender", "Nad-Test");
Message message1 = new Message("Hello AMQP!");
TimeSpan ts = new TimeSpan(0, 5, 0);
sender.Send(message1, ts);
Console.WriteLine("Message sent into queue Nad-Test");
}
When running the above, the instance is being created as shown, but as you can see, the Queue message is indicating 0;
Can anyone advise on the above?
Nad-Test has been created but no Queue Count and it is in MultiCast mode.
With .NET Client for ActiveMQ Artemis, it should be as simple as that:
var connectionFactory = new ConnectionFactory();
var endpoint = Endpoint.Create("tst01sacmamq.corporate.intra", 61616);
var connection = await connectionFactory.CreateAsync(endpoint);
var producer = await connection.CreateProducerAsync("Nad-Test", RoutingType.Anycast);
var msg = new Message("Hello AMQP!")
{
TimeToLive = TimeSpan.FromMinutes(5)
};
await producer.SendAsync(msg);
This Client is based on AmqpNetLite but it handles all the ActiveMQ Artemis minutiae for you.
https://www.nuget.org/packages/ArtemisNetClient
https://havret.github.io/dotnet-activemq-artemis-client/

Azure Service Bus SubscriptionClient.OnMessage always completes message when it shouldnt

I am trying to receive all messages for a given subscription to a Service Bus Topic, but for the context of this app I do not want them dead lettered at this time, I just want to view them and leave them on the subscription. Despite instantiating the Client as
SubscriptionClient sc = SubscriptionClient.CreateFromConnectionString(connectionString, sub.topicName, sub.subscriptionName, ReceiveMode.PeekLock);
and making sure that I am using message.Abandon() rather than message.Complete() the message always gets Dead-lettered after accessing the message. I also have options.AutoComplete set to false
full method code below:
public List<ServiceBusMessage> RetrieveSubscriptionMessages(Subscription sub) {
ServiceBusMessage sbm;
List<ServiceBusMessage> list = new List<ServiceBusMessage>();
String connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"].ToString();
SubscriptionClient sc = SubscriptionClient.CreateFromConnectionString(connectionString, sub.topicName, sub.subscriptionName, ReceiveMode.PeekLock);
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
sc.OnMessage((message) => {
try {
sbm = new ServiceBusMessage() {
topicName = sub.topicName,
messageText = message.GetBody<String>()
};
list.Add(sbm);
message.Abandon();
}
catch (Exception) {
message.Abandon();
throw;
}
}, options);
return list;
}
Am I missing something ? Or is there an issue with auto dead-lettering with the onMessage() method?
Thanks !
When a message is abandoned the service bus will immediately make it available for re-delivery to any subscriber of the topic.
If you are trying to configure a multicast mechanism in which multiple listeners all receive the same message, then understand that all listeners on a given subscription will be competing for the same message. In order for every listener to receive its own copy of the message, then simply create a unique subscription to the topic for each listener.
If your intent is to delay re-delivery of the abandoned message, you might look at the SO question: What's the proper way to abandon an Azure SB Message so that it becomes visible again in the future in a way I can control?

Why am I looping in ActiveMQ QueueBrowser?

This snippet keeps looping through the queue browser. Apache.NMS 1.5.1, Apache.NMS.ActiveMQ 1.5.6, Broker 5.8.0
Queue size is < 200
I checked prefetch, and it is still at default.
ActiveMQ with C# and Apache NMS - Count messages in queue suggests checking enumerator.Current in the loop, but I am already doing that.
I tried setting a large prefetch with ?jms.prefetchPolicy.all=50000, but it still loops.
IConnectionFactory connectionFactory = new ConnectionFactory(connectUri, "SNDTest");
using (IConnection conn = connectionFactory.CreateConnection(USERNAME, PASSWORD))
{
conn.Start();
using (ISession session = conn.CreateSession())
{
using (IQueueBrowser browser = session.CreateBrowser(errorQueue))
{
int i = 0;
var e = browser.GetEnumerator();
while (e.MoveNext())
{
i++;
IMessage m = e.Current as IMessage;
How do I stop the looping?
This could be related to some bugs that are fixed in the v5.9.0 snapshot builds such as, AMQ-4487. Try downloading a recent nightly build of the broker and running against that.

Should ServiceBroker conversations be inside a transaction

We have some code that looks a bit like this (error handling and other things removed)
using (var tran = conn.BeginTransaction())
{
var client = new Service(...);
var dialog = client.GetConversation(null, conn, tran);
var response = dialog.Receive();
// do stuff with response, including database work
dialog.Send(message, conn, tran);
dialog.EndConversation(conn, tran);
tran.Commit();
conn.Close();
}
We've inherited this code and aren't experts in ServiceBroker, would there be problems if we moved the conversation outside of the transaction like this:
var client = new Service(...);
var dialog = client.GetConversation(null, conn, tran);
var response = dialog.Receive();
using (var tran = conn.BeginTransaction())
{
// do stuff with response, including database work
tran.Commit();
}
dialog.Send(message, conn, tran);
dialog.EndConversation(conn, tran);
conn.Close();
In this case you receive message and its gets removed from the queue. You will not be able to receive it again..
If all code is in transaction and there is error in message processing- transaction never commits and message stays in queue (by default- after 5 rollbacks queue gets disabled). So you can detect the reason of error, correct it and process message again (expected exceptions should not cause rollback, there are quite a few ways to handle them).
I would say that everything should be in transaction.

Categories