0
votes

I'm very new to NserviceBus and I have an NServiceBus application which processes a message with a command that creates multiple child messages containing a different command.

For example I put message BulkOrder01 on the queue which gets picked up by my BulkOrder message handler. The payload of my BulkOrder01 conatins an bulk order ID which when looked up in a database returns 4000 orders. For each of those child orders I send an Order message to the queue.

This seems to work fine in our production environment for less than 1000 child orders but once we get above 1000 we often see the parent message not being processed and therefore the child messages not being created.

When running it locally, I have found that it will send one or two of the child messages to the queue but then will exception with an NServiceBus.Unicast.Queing. FailedToSendMessgeException 'Failed to send message to address: Namespace.OrderService@MyComputername' and the inner exception is 'System.Messaging.MessageQueueException Cannot enlist the transaction'

I have found that if I set DoNotWrapHandlersExecutionInATransactionScope in the EndpointConfig then I do not get the exception until what appears to be the parent message times out. Which I can prevent by increasing the transaction timeout.

However setting DoNotWrapHandlersExecutionInATransactionScope makes me nervous, I can't seem to find much information about what this actually does. Obviously it does not wrap handlers execution in a transaction scope but from the testing I have performed it still appears to behave transactionally so if the parent message fails then none of the child messages get sent. I remember reading that there are multiple layers of transaction scope - so does this just remove one of those layers?

Maybe this whole approach the wrong way of going about it - I know about the existence of Sagas within the NserviceBus but nothing really about them maybe the process i have described should be done using a Saga...

Googling the exception bought up that it is a timeout issue, however I have found that only increasing the timeout on it's own simply delays the exception by the timeout amount. It only works locally when I have DoNotWrapHandlersExecutionInATransactionScopeset. In rpoduction it seems to work more reliably and only fails on large volumes of child messages.

Also the child messages that are created seems to take a fairly long time to be added to the queue, around 50 milliseconds which when scaled up to 4000 messages is a total of 3.3 minutes. This seems a long time to put a small message onto the queue maybe something is not configured correctly?

I am using Entity Framework for access to the database and Unity for dependency injection within a C# 4.5 environment running NServiceBus 4.3.0.0

I am using IBus.SendLocal to send the messages and I'm configuring the timeout and setting as follows:

NServiceBus.Configure.Transactions.Advanced(x => x.DefaultTimeout(new TimeSpan(0, 5, 0)));

NServiceBus.Configure.Transactions.Advanced(x => x.DoNotWrapHandlersExecutionInATransactionScope());

Can anyone point me in the right direction as to if I'm doing this correctly -is the slow(ish) performance expected. Thanks.

1

1 Answers

1
votes

We have a similar process and the challenge is that all of that work is wrapped up in the same transaction as you have found out. You will want to create another endpoint to handle your child messages.

I would recommend configuring the Distributor in NSB and have the Distributor delegate all the child processing to workers. You can therefore scale out the processing of child messages as needed.