7

I am dealing with a strange issue related to NHibernate and distributed transactions in a WCF service. See Deadlocks causing 'Server failed to resume the transaction' with NHibernate and distributed transactions for more details.

One thing that seems to solve my problem is using NHibernate's AdoNetTransactionFactory, instead of AdoNetWithDistributedTransactionsFactory.

I believe that the AdoNetWithDistributedTransactionsFactory is involved with making NHibernate's second-level caching mechanism work right, but we're not using that. What (if any) other problems exist with using AdoNetTransactionFactory with distributed transactions?

Thanks for your time!

Community
  • 1
  • 1
jon without an h
  • 572
  • 1
  • 4
  • 17

3 Answers3

1

I notice that you mentioned from your other question/answer:

SqlConnection class is not thread-safe, and that includes closing the connection 
on a separate thread. Based on this response we have filed a 
bug report for NHibernate.

However, from NHibernate's documentation:

11.2. Threads and connections

You should observe the following practices when creating NHibernate Sessions:

  • Never create more than one concurrent ISession or ITransaction instance per database connection.

  • Be extremely careful when creating more than one ISession per database per transaction. The ISession itself keeps track of updates made to loaded objects, so a different ISession might see stale data.

  • The ISession is not threadsafe! Never access the same ISession in two concurrent threads. An ISession is usually only a single unit-of-work!

    If you are trying to multi-thread the connection with NHibernate perhaps it is just not going to work. Have you considered a different ORM such as Entity Framework?

    No matter what ORM you choose though, the database connection will not be thread safe. This is universal.

    "many DB drivers are not thread safe. Using a singleton means that if you have many threads, they will all share the same connection. The singleton pattern does not give you thread saftey. It merely allows many threads to easily share a "global" instance." - https://stackoverflow.com/a/6507820/1026459

  • Community
    • 1
    • 1
    Travis J
    • 81,153
    • 41
    • 202
    • 273
    • 2
      Travis, we were following all of the NH multithreading guidance. The problem was within NH itself - if the distributed transaction was aborted by the DB server, NH would close the connection on a different thread. – jon without an h Oct 01 '12 at 15:25
    1

    Using AdoNetTransactionFactory with distributed system transactions will cause those transaction to be ignored by NHibernate, which has the following consequences:

    • ConnectionReleaseMode.AfterTransaction will not be honored. Instead, NHibernate will release the connection after each statement, and so will re-acquire a connection from the pool for the next one. Depending on your data provider, this may trigger escalation of the transaction to distributed.
    • FlushMode.Commit will not be honored. Explicit flushes will be required instead. (Auto flushes before queries may still occur.)
    • Works needing to be isolated from current system transaction will still be included inside it. (Unless the connection string Enlist property is false.) Such works may include id generators queries such as retrieving the next high value for a table hilo generator. If the transaction gets roll-backed, NHibernate may then use conflicting ids.
    • The NHibernate session will not be able to correctly track locks it holds on entities. Considering itself outside of a transaction, it will consider it has no lock on them. So it may try (on user code request by example) to re-lock them with lower lock level than the one the transaction already holds on them in database. Not sure what outcome could result of that. (At best, ignored, at worst...)
    • Second level cache will be disabled as soon as you start modifying data. NHibernate sort of "invalidate" cache entries in such situation, and re-enable them only on transaction completion, updated. But since it will not be aware of transactions...
    • Some extensions (maybe Envers) may rely on NHibernate transaction events, and will no more work as expected.
    Frédéric
    • 9,364
    • 3
    • 62
    • 112
    0

    I strongly recommend upgrading to nhibernate 3.2(or a version close to it). Why? Since 2.1, there has been significant improvements (read rewrite) to the AdoNetWithDistributedTransactionFactory. Matter of fact, it now handles TransactionScopes/ambient-transactions and the like correctly. When we ran 2.1 in production we encounter many issues related to distributed transactions. We pretty much had to fix a ton of stuff ourselves and recompile NHibernate. 3.2 seems to have fixed many issues around the subject.

    I don't have the source near me but, if memory doesn't fail me, the AdoNetTransactionFactory doesn't check/handle ambient transactions. So, you are down to NHibernate booting transactions when one is not present in the session(by means of ISession.BeginTransaction()).

    Newbie
    • 7,031
    • 9
    • 60
    • 85
    • Thanks for the response. I'm assuming you read my other question where I mentioned we're using NH2.1. We actually did try upgrading to 3.2 and it did not fix our issues. – jon without an h Dec 28 '11 at 16:57
    • In our testing, we found that the AdoNetTransactionFactory is not aware of ambient transactions, but the underlying SqlConnection still participates in it, and so everything still works even if we don't use an NH transaction. I believe that the AdoNetWithDistributedTransactionsFactory is mostly involved in making sure NH's second-level caching mechanism works right, but I'd like to know if there are any other issues with it. – jon without an h Dec 28 '11 at 16:58
    • AdoNetWithDistributedTransactionFactory will auto-enlist in ambient transaction in more cases, and will _sometimes_ work without using an NHibernate transaction. Though unfortunately there even in 3.3 some issues so it is still recommended to always use NHibernate transaction in addition to TransactionScope. I fear there might be some issues with the current approach that is inherently unfixable, due to limitations in System.Transactions and System.Data. – Oskar Berggren Sep 20 '12 at 15:03