Mark Needham

Thoughts on Software Development

Archive for the ‘nhibernate’ tag

NHibernate 2nd level cache: Doing it wrong?

with 2 comments

I wrote a couple of weeks ago about how we’d been trying to make use of the NHibernate 2nd level cache and we were able to cache our data by following the various posts that I listed.

Unfortunately when we ran some performance tests we found that the performance of the application was significantly worse than when we just wrote our own ‘cache’ – an object which had a dictionary containing the reference data items we’d previously tried to lookup and the appropriate values.

We don’t need to handle cache invalidation. The client’s policy is to restart production servers every night so if we want to update any of the reference data then we just need to make sure a database script is run before the servers get restarted.

Explicit transactions when reading

There is a post on the NHibernate Profiler website which describes why we should not use implicit transactions when using NHibernate. Instead we should create explicit ones:

When we don’t define our own transactions, it falls back into implicit transaction mode, where every statement to the database runs in its own transaction, resulting in a large performance cost (database time to build and tear down transactions), and reduced consistency.

Even if we are only reading data, we should use a transaction, because using transactions ensures that we get consistent results from the database.

We ended up with something like this:

public class OurRepository
{
	public ReferenceDataObject Find(ReferenceDataObjectKey key)
	{
		using(var session = SessionFactory.OpenSession())
		{
			using(var tx = session.BeginTransaction())
			{
				var query = session.Linq<ReferenceDataObject>().Where(r => r.Key == key);
				query.QueryOptions.SetCachable(true).SetCacheMode(CacheMode.Normal);
				// and so	 on
			}
		}
 
		// return the object
	}
}

As well as performance tests, we found that our integration tests became much slower than when we used our own ‘cache’.

We have some tests which look up 100s of different bits of reference data and the total time taken to run those tests went from around 4 seconds up to 30 seconds.

As I understand it, putting a transaction around a query means that we create a transaction with the database on every request even if the query is cached and we’re going to retrieve the results from the 2nd level cache rather than the database.

We took out the transaction which reduced the time taken to 7 seconds but it was still slower than when we used our hand rolled cache.

It seems like we must be doing something wrong or not understand something with respect to the NHibernate 2nd level cache because it seems ridiculous that the performance could be this different?

Written by Mark Needham

June 29th, 2010 at 6:45 am

Posted in Hibernate

Tagged with

Fluent NHibernate and the 2nd level cache

with 6 comments

We’ve been trying to cache some objects using NHibernate’s second level cache which always proves to be a trickier task than I remember it being the previous time!

We’re storing some reference data in the database and then using LINQ to NHibernate to query for the specific row that we want based on some user entered criteria.

We can cache that query by calling ‘SetCacheable’ on the ‘QueryOptions’ property of our query:

1
2
3
4
5
6
7
8
9
public class OurRepository
{
	public ReferenceDataObject Find(string criteria1, string criteria2)
	{
		var query = session.Linq<ReferenceDataObject>();
		query.QueryOptions.SetCachable(true).SetCacheMode(CacheMode.Normal);
		// and so on
	}
}

That helps to ensure that if we do the exact same query again it won’t go to the database again. Instead it will just retrieve the id of the appropriate object from the 2nd level cache and then go and retrieve that.

In order to ensure that the ‘ReferenceDataObject’ does not get retrieved from the database each time we need to ensure that it is cached as well.

We can do that by adding the following to its mapping file:

public class ReferenceDataObjectMappings : ClassMap<ReferenceDataObject>
{
	...
 
	Cache.ReadOnly();
 
	// or
 
	Cache.ReadWrite();
 
	// depending on whether we want to update the value in the cache based on it changing in the database or not
 
	..
 
}

The next step is to ensure that we have the query cache and second level cache turned on in the place where we configure our session factory:

Fluently
.Configure()
.Database(MsSqlConfiguration.MsSql2000.ConnectionString("connection string")
	.Cache(c => c.UseQueryCache().ProviderClass(typeof(NHibernate.Caches.SysCache.SysCacheProvider).AssemblyQualifiedName)))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ICurrentSessionFactory>())
.BuildSessionFactory();

The ‘SysCacheProvider’ is one we can use for web applications although there are all sorts of others available too.

Having got all this setup we wrote an integration test which added an item to the reference data table and then tried to retrieve it several times with the theory being that we would only see one call to the database.

[Test]
public void ShouldCacheTheReferenceData()
{
	var myReferenceObject = new ReferenceDataObject(...);
 
	using(var session = SessionFactory.OpenSession())
	{
		session.Save(myReferenceDataObject);
		session.Flush();
	}
 
	// try and retrieve the object a few times here
}

Unfortunately we were seeing the database being hit every single time which confused us greatly until we came across the following explanation on Gabriel Schenker’s blog:

A common error (It happened to me as well!) is to forget to commit or omit a transaction when adding or changing an entity/aggregate to the database. If we now access the entity/aggregate from another session then the 2nd level cache will not be prepared to provide us the cached instances and NHibernate makes an (unexpected round trip to the database).

We need to change our test to commit the object in a transaction if we want the object to be propagated down to the 2nd level cache:

[Test]
public void ShouldCacheTheReferenceData()
{
	var myReferenceObject = new ReferenceDataObject(...);
 
	using(var session = SessionFactory.OpenSession())
	{
		using(var tx = session.BeginTransaction())
		{
    			session.Save(myReferenceObject);
    			tx.Commit();        // important otherwise caching does NOT work!
		}
	}
 
	// try and retrieve the object a few times here
}

Written by Mark Needham

June 16th, 2010 at 12:07 am

Posted in Hibernate

Tagged with ,

Fluent NHibernate: Seeing the mapping files generated

without comments

We’ve been fiddling around with Fluent NHibernate a bit over the last couple of days and one of the things that we wanted to do was output the NHibernate mapping files being generated so we could see if they were as expected.

I couldn’t figure out how to do it but thanks to the help of James Gregory, Andrew Bullock and Matthew Erbs on twitter this is the code that you need in order to do that:

1
2
3
4
5
Fluently
.Configure()
.Database(MsSqlConfiguration.MsSql2000.ConnectionString("connection string"))
.Mappings(m => m.FluentMappings.ExportTo("c:\\directory-to-output-files-to"))
.BuildSessionFactory();

The 4th line is the important one here and we can choose which directory the mappings files get outputted to and then check that everything is getting setup as we’d expect.

Written by Mark Needham

June 15th, 2010 at 11:15 pm

Posted in Hibernate

Tagged with