Is NServiceBus (AsA_Server) without DTC possible?
Asked Answered
K

4

6

I am using NServiceBus for the first time and have a small, simple application where a user submits a form, the form fields are then sent to the queue, and the handler collects this data and writes it to the database using linq-to-sql.

Any changes within Component Services is a complete no-no as far as the DBA is concerned, so I'm now looking for an alternative to DTC (which is not enabled on the DB server), but using AsA_Server so that messages do not get purged.

I have tried removing AsA_Server after IConfigureThisEndpoint and specifying the configuration myself, but this doesn't seem to work (the console appears, page loads but nothing happens, it doesn't even stop at breakpoints.) AsA_Client does work, but as I understand it the messages will be purged at startup which I need to avoid.

Any suggestions?

Thanks,

OMK

EDIT: This has now been resolved by using wrapping the call to the database in a suppress transaction scope, which allows the database work to be done with no ambient transaction to enlist in:

using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Suppress)) 
{ 
     // code here 
     sc.Complete(); 
} 
Korwun answered 19/7, 2011 at 13:46 Comment(0)
A
9

When you use AsA_Server, you are specifying you want durable queues and you will need to configure transactional queues.

With a transactional send/receive MSMQ requires you to send, transmit, receive, and process as part of one transaction. However, actually all these stages take place in their own transactions.

For example, the send transaction is complete when the sender sends a message onto their local MSMQ subsystem (even if the queue address is remote, the sender still sends to a local queue which acts as a kind of proxy to the remote queue).

The transmit transaction is complete when the MSMQ subsystem on the senders machine successfully transmits the message to the MSMQ subsystem on the receivers machine.

Even though this may all happen on one machine, I am guessing that your Handle() method is writing to a database on a different machine.

The problem here is that for the receive operation to complete satisfactorily from a transaction perspective, your call to the database must be successful. Only then will the message be de-queued from your input queue. This prevents any chance that the message is lost during processing failure.

However, in order to enforce that across the network you need to involve DTC to coordinate the distributed transaction to the database.

Bottom line, if you want durable queues in a distributed environment then you will need to use MSDTC.

Hope this helps.

Abacus answered 19/7, 2011 at 22:30 Comment(9)
Thank you for your response and for explaining the necessity of MSDTC - it's much appreciated. It looks as though I've hit a brick wall then with trying to implement NServiceBus, as configuring the DB server 'is not an option' to quote the DBA in more polite terms! :o) Unless the business is happy to go with AsA_Client of course. Other than losing any messages in the queue, is there any other issues we would face using AsA_Client as opposed to using AsA_Server? Thanks again.Korwun
If I were you I would get your superiors to intervene. Not allowing MSDTC on a database server to support remote transactions is a bit unreasonable in my opinion. You can enable DTC without openning the whole thing up (mutual authentication between caller and server can be guaranteed). To answer your other question, you won't face any further issues using AsA_Client.Abacus
Another possible solution is rather than make the database call directly, instead somehow invoke some asynchronous process in your Handle() implementation. Perhaps some "offline" data writer service. This would return immediately, thus allowing your receive transaction to commit. Then the other process can write the data away in an "offline" manner.Abacus
Of course! :o) I've just come across the following code which is now writing my data to the DB. Are there any potential issues with it that you know of? using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Suppress)) { // code here sc.Complete(); } Thanks.Korwun
In answer to your question - your call to the DB will be made using no transaction. providing you don't put any error handling around your call then any errors on the SQL side should be bubbled back to you and the message will be rolled back to the queue. The only drawback is that you don't have an iron-clad guarantee that the SQL side committed the data successfully. However, if you are calling a stored proc then the risk of this happening is tiny. Well done, you have found a work-around! Please update your original post with an "Edit:..." to reflect your solution so that others can benefit.Abacus
It says I can't upvote because I need a Reputation of 15. :o(Korwun
It interesting to hear that a DBA has the power to make the decision to risk loosing business data. Usually that decision belongs to the business and not to the tech guys. Hopefully you're not processing any data that is critical for your business!Tackle
it is true that even with suppressing transactions, you shouldn't have a problem with losing the message as stated by hugh because the exception on talking to the database should lead to aborting the message queue transaction. there is another potential problem, though, in that the database transaction can complete but the message queue transaction can still abort. consider, for example, a power loss after committing the database transaction. in this case, the message queue transaction will abort and you will receive this message twice and perform the same transaction twice.Divorcement
This is true and is why you should try to always ensure that your message endpoints are idempotent (can process the same data more than once and retain consistency)Abacus
C
7

There is an alternative. In your connection string you can add the option to not enlist in a distributed transaction and this will have your DB connection ignored in the DTC.

Of course, if this is set in the config then all database transactions for the application are ignored by the DTC rather than just a specific one.

Example:

<add key="DatabaseConnectionString" value="Data Source=SERVERNAME;Initial Catalog=DBNAME;Integrated Security=True;Enlist=False"/>
Chan answered 1/3, 2012 at 6:2 Comment(2)
+1 this is also a great option to use if your endpoint is only reading data eg for a message enricher.Cooee
This is an excellent answer. It solved all my problems. I was hereby able to disable DTC only for a single database that didn't support it.Mouldy
V
7

With NServiceBus 4.0 you can now do the following, which finally worked for me:

 Configure.Transactions.Advanced(t =>
                {
                    t.DisableDistributedTransactions();
                    t.DoNotWrapHandlersExecutionInATransactionScope();
                });
Vermifuge answered 28/10, 2013 at 7:48 Comment(0)
J
6

When you use the As (AsA_Client, AsA_Server) interfaces, the configuration is applied after Init() so all the settings that you make there regarding MsmqTransport and UnicastBus are overriden.

It's possible to override those settings using IWantTheConfiguration in a IHandleProfile implementation. You get the Configuration after the default roles are applied but before the bus is started.

This way you can change the default profile settings and tailor them to your needs: deactivate transactions, enable impersonation...

Example:

public class DeactivateTransactions : IHandleProfile<Lite>, IWantTheEndpointConfig
{
    private IConfigureThisEndpoint configure;

    public IConfigureThisEndpoint Config
    {
        get { return configure; }
        set
        {
            this.configure = value;

            Configure.Instance.MsmqTransport()
                .PurgeOnStartup(false)
                .IsTransactional(false); // Or other changes
        }
    }

    public void ProfileActivated()
    {
    }
}
Jude answered 19/9, 2011 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.