Tuesday, 13 November 2007

nHibernate and multithreading - I

nHibernate is no doubt is a great open source utility to encapsulate database connectivity. It has lot of feature that can make your system robust. You can read more details about nHibernate from nHibernate.org.

But when it comes to multi-threading programming you have to be very cautious in your development. There are plenty of place it can work differently than you want it to be. Concurrency is a big think to keep in mind. I don't mean that nHibernate can't handle multi-threading or concurrent request. Rather is very efficient in multi threading, but the programmer has to be vigilant while setting properties and use method call to manipulate or to retrieve data.



In this post I want to discuss about stale data in multithreaded environment cache. Lets consider a web service which exposes two methods UpdateUser() to update user profile and GetUser() to retrieve a user details. In a typical scenario application uses this web service will make a call to UpdateUser and then GetUser to read updated details.

In some scenario it might happen that the GetUser return old user profile, instead of latest updated one. At a first glance it might look impossible, as you are updating first and then calling get. But it happens.

Lets see what going on the the web service side. When any one makes a request to a web service web server handle using separate thread. In most scenario server uses thread pooling to minimise thread creation overhead. Along with that as nHibernate suggests the Session is not shared among threads. the resultant is that the primary cache is separate for separate threads. resultant is calls reading data from different data cache. So now even if GetUser is called after UpdateUser if they are handled by different thread in the server, the UserProfile object may be out of sync. This is happens b'cus nHibernate try to read data from cache if it's available.

To solve this issue caller simply has make sure that nHibernate picks up the data from database not from the cache. This can be done in very simple way. Use pessimistic Locking. ISession provides Lock() to do that.

example:

work in thread one:

ISessionFactory sessions;
IList profiles;
UserProfile profile;
....
ISession session = sessions.OpenSession();
...
ICriteria criteria = session.CreateCriteria(typeof(UserProfile));
profiles= criteria.List();

....
//do some updating

Profile profile = profiles[0];
profile.HomePhone = "0208 212 3333334";
session.SaveOrUpdate(profile);


while reading data:

Profile profileToRead = profiles[0];
session.Lock( profileToRead, LockMode.Read); // this will ensure profile updating, and will not return stale data.

In this scenario Session does a version number check of the cache object with actual data store. If they are different it updated the cache with latest version of the object and thus it always returns updated data.

No comments:

Post a Comment