nHibernate–manual session flushing

An nHibernate session maintains all changes to the object model. At some point, it needs to synchronize these changes with the database. It can either do these constantly (as and when it encounters a new change in the object model) – or it can wait till it has a whole batch of changes to commit. The latter is an obvious choice – since it avoids multiple database roundtrips. This ‘synchronization’ is called flushing and it happens at various points in a session’s lifecycle. While it is a good thing, if you have TOO MUCH flushing, you may end up with a performance degradation.

For example, if you have a complex query (which gets broken into many smaller queries by nHibernate), you drastically increase the number of times that session.flush() is called by nHibernate. The problem is :

Each session.flush() call is very expensive.

For your complex queries, you may be looking at a few dozen times that this session.flush() call takes place. This really slows down the round trip query execution time (although in reality, the actual SQL execution has nothing to do with it – it is simply nHibernate’s bookkeeping).

Turn off automatic flushing

This is the single , most important performance improvement you can make in nHibernate. Turning automatic flushing off means you will manually have to decide when and where to flush(). This is simple – just before you start your transaction make sure everything is in sync by flushing once. Now turn off automatic flushing. And right before you are ready to commit your transaction (after you have saved your session), go ahead and turn flushing back on. Here is a sample :

Just before you start your transaction

  1. Manually flush and
  2. Turn off auto flushing

var session = SessionManager.GetSession();

 

            // Right before you begin your transaction, flush the session and turn off automatic flushing

            session.Flush();

            session.FlushMode = NHibernate.FlushMode.Never;

 

            using (var transaction = session.BeginTransaction())

            {

            

Just before you commit,

  1. Turn automatic flushing back on
session.Save(nomination);

 

               // turn on automatic session flushing 

               session.FlushMode = NHibernate.FlushMode.Auto;

               transaction.Commit();

What can go wrong?

Remember, you are temporarily suspending synchronizing the database with your object changes. This is not an issue for your retrieval (get) queries, but for saves, updates, deletes – you need to watch out for specific scenarios where you may have transient data (data that is not yet committed – but you try to access anyway). An example of such a transient piece of data is a private property ID (private so that the database autogenerates the ID). If you have an ID that is to be autogenerated – and you try to access that ID before the commit takes place, you will end up in this ‘exception’ scenario. Suppose your logic entails creating a new Product (say a new Financial Product Offering) – and then associating existing customers with it (perhaps as PROSPECTS for this product). Since your productID is not yet committed, associating the customers will not work – and will throw an exception of the following type:

nhibernate_transient_exception

NHibernate.TransientObjectException: object references an unsaved transient instance – save the transient instance before flushing or set cascade action for the property to something that would make it autosave. Type:

All this means is that nHibernate does not know what to do with the object changes (since flushing has been turned off). To counter this, you must revisit your business logic and try and figure out why you needed to access something that hadn’t been committed yet. If possible, change the business logic. If not possible, then simply remove the manual flushing code above and revert to automatic flushing. This scenario, though possible, is more of an exception than the rule – and generally speaking, the manual flushing is a safe change to make.

In addition to revisiting your business logic, one recommended change (for your transaction.commit()) is to surround it with a try-catch. The try catch will allow a rollback in case you end up with any failed parts within your transaction. All you need is :

   1:  

   2: try

   3: { 

   4:      transaction.Commit();

   5: }

   6: catch (Exception ex)

   7: {

   8:     transaction.Rollback();

   9:     throw;

  10: } 

After all, rollbacks are the reason you are using transactions in the first place. If something goes wrong, you have the option of rolling back the transaction to avoid inconsistent states.

Summary

There are a few areas where nHibernate settings can affect your data tier performance. The most significant one that I discovered (during a production troubleshooting assignment of a web app) was this automatic and frequent session  flushing. This can be easily circumvented by a few lines of code change.  It does speed things up – but comes with a potential drawback – which affects saves and updates  (not the retrievals). The saves and updates can possibly run into a situation where nHibernate does not know what to do with a transient object. It throws a meaningful exception – which can be addressed using the suggestions above.

In spite of this potential drawback, manual session flushing is something I would still recommend – due to the significant performance gain. The files affected by the potential drawback can be easily addressed by either disabling manual flushing – or reworking the business logic that causes the exception to occur.

Cloud Advisory Services | Security Advisory Services | Data Science Advisory and Research

Specializing in high volume web and cloud application architecture, Anuj Varma’s customer base includes Fortune 100 companies (dell.com, British Petroleum, Schlumberger).

All content on this site is original and owned by AdverSite Web Holdings, Inc. – the parent company of anujvarma.com. No part of it may be reproduced without EXPLICIT consent from the owner of the content.

Anuj Varma – who has written posts on Anuj Varma, Technology Architect.


1 Comment

  • Hey Anuj, thanks for this article. I’ve searched all over for help with that exception. In my case I was getting the TransientObjectException in a method that only did a select. I was pretty confused but the stack trace showed me that while doing the select, AutoFlush was called. So I simply set autoflush to Never, performed the select, then set it back to Auto. Worked like a charm..

Leave a Reply

Your email address will not be published. Required fields are marked *