Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 11 | Comments: 40

filter by tags archive

NH Prof AlertsUse of implicit transactions is discouraged

time to read 2 min | 399 words

This is a bit from the docs for NH Prof, which I am sharing in order to get some peer review.

A common mistake when using a database is that we should use only transactions to orchestrate several write statements. Every operation that the database is doing is done inside a transaction. This include both queries and writes ( update, insert, delete ).

When we don't define our own transactions, we fall back into implicit transaction mode, in which every statement to the database run in its own transaction, resulting in a higher performance cost (database time to build and tear down transactions) and reduced consistency.

Even if we are only reading data, we want to use a transaction, because using a transaction ensure that we get a consistent result from the database. NHibernate assume that all access to the database is done under a transaction, and strongly discourage any use of the session without a transaction.

Example of valid code:

using(var session = sessionFactory.OpenSession()) 
using(var tx = session.BeginTransaction()) 
{ 
	// execute code that uses the session 
	tx.Commit(); 
} 

Leaving aside the safety issue of working with transactions, the assumption that transactions are costly and we need to optimize them is a false one. As already mentioned, databases are always running in transaction. And databases have been heavily optimized to work with transactions. The question is whatever this is per statement or per batch. There is some amount of work that need to be done to create and dispose a transaction, and having to do it per statement is actually more costly than doing it per batch.

It is possible to control the number and type of locks that a transaction takes by changing the transaction isolation level (and indeed, a common optimization is to reduce the isolation level).

NHibernate treat the call to Commit() as the time to flush all changed items from the unit of work to the database, without an explicit Commit(), it has no way of knowing when it should do that. A call to Flush() is possible, but it is generally strongly discouraged, because this is usually a sign that you are not using transactions properly.

I strongly suggest that you would use code similar to the one shown above (or use another approach to transactions, such as TransactionScope, or Castle's Automatic Transaction Management) in order to handle transactions correctly.

More posts in "NH Prof Alerts" series:

  1. (31 Dec 2008) Use statement batching
  2. (30 Dec 2008) Too many database calls per session
  3. (29 Dec 2008) Excessive number of rows returned
  4. (29 Dec 2008) Unbounded result set
  5. (28 Dec 2008) Use of implicit transactions is discouraged
  6. (28 Dec 2008) Select N + 1

Comments

Steve Bohlen

To me this is a (somewhat) confusing section of the docs without an assumption that a reader already has a good understanding of the difference between an NHibernate transaction and a database transaction.

Recognizing that this is bound to be a recurring theme with this product (you will need to assume SOME level of base NH understanding else your recommended 'solutions' to the problems pointed out by the NH Profiler won't be comprehensible to users of the software), I wonder if this (transaction mgt) is an area that might be worth extrapolating on a bit to provide some more background....?

I got what you were saying, but I'm uncertain that other more 'casual' NH users necessarily would.

My $0.02.

Andyk

Hi Oren,

in your last statement you recommend using x, y, z approaches. But does that mean then that you're now discouraging the unit of work pattern? Because you dont mention this at all?

Ayende Rahien

Steve,

Can you expand more about why you found this confusing and how I can make this less so?

Ayende Rahien

AndyK,

There is a difference between transactions and Unit of Work. A Unit of Work is usually composed of one or more transactions. This topic is focusing on something that is lower level than the unit of work.

Anders Lybecker

Hi Oren,

I agree, as long as the transactions are kept short. Otherwise it is better to use implicit transactions for reading.

Another note about the transaction isolation level: I wouldn't call it optimization to reduce the isolation level - you have to think upfront about the use of isolation levels.

In most cases when reducing isolation level, the cause is often nasty stuff like deadlocks etc.

Do consider readcommitted snapshot in SQL Server 2005+ when selecting transaction isolation level (similar to row versioning in the Oracle World which is default).

You should still keep your transactions short with readcommitted snapshot, otherwise you would be hammering the tempdb.

Jack

"call to Flush() is possible, but it is generally strongly discouraged, because this is usually a sign that you are not using transactions properly."

This is true if you're using NHibernate's transaction manager, but if we're using TransactionScope, we need to explicitly called Flush.

Ayende Rahien

Not really.

When you Complete() the TransactionScope, NH will handle this for you

Jan Van Ryswyck

Isn't TransactionScope support only since NH 2.1?

Ayende Rahien

Jan,

Yes & No.

It sort of works in 2.0, if you don't do things that break it.

Jan Van Ryswyck

In that case, I did something that made NH angry :-). I'm thinking about your advice to use a NH transaction for both read and write operations. This makes complete sense to me.

However, I'm feeling a bit reluctant when it comes to TransactionScope as it can turn to distributed transactions without any transparent knowing from the developer. Using the DTC is rather costly I guess?

Ayende Rahien

Using DTC is costly, and I wouldn't recommend it unless you are truly orchestrating several resources.

Tim Barcz

In the code:

using(var session = sessionFactory.OpenSession())

using(var tx = session.BeginTransaction())

{

// execute code that uses the session 

tx.Commit(); 

}

Won't this disallow for lazy loading because the session will be gone when it leaves the scope of the "using"?

Ayende Rahien

Yes, it would. And for a good reason.

You should manage your loading better. Transactions should be at the controller level, and scoped to include everything that goes on there.

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. Production postmortem: The case of the lying configuration file - one day from now
  2. Production postmortem: The industry at large - about one day from now
  3. The insidious cost of allocations - 3 days from now
  4. Find the bug: The concurrent memory buster - 4 days from now
  5. Buffer allocation strategies: A possible solution - 7 days from now

And 3 more posts are pending...

There are posts all the way to Sep 10, 2015

RECENT SERIES

  1. Find the bug (5):
    20 Apr 2011 - Why do I get a Null Reference Exception?
  2. Production postmortem (10):
    31 Aug 2015 - The case of the memory eater and high load
  3. What is new in RavenDB 3.5 (7):
    12 Aug 2015 - Monitoring support
  4. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats