Ayende @ Rahien

It's a girl

NHibernate 2nd Level Cache

NHibernate has a builtin support for caching. It sounds like a trivial feature at first, until you realize how significant it is that the underlying data access infrastructure already implements it. It means that you don’t have to worry about thread safety, propagating changes in a farm, built smart cache invalidation strategies or deal with all of the messy details that are usually along for the ride when you need to implement a non trivial infrastructure piece.

And no, it isn’t as simple as just shoving a value to the cache.

I spent quite a bit of time talking about this here, so I wouldn’t go about all the cache internals and how they work, but I’ll mention the highlights. NHibernate internally has the following sub caches:

  • Entity Cache
  • Collection Cache
  • Query Cache
  • Timestamp Cache

NHibernate make use of all of them in a fairly complex way to ensure that even though we are using the cache, we are still presenting a consistent view of the cache as a mirror of the database. The actual details of how we do it can be found here.

Another thing that NHibernate does for us when we update the cache is try to maintain the consistent view of the world even when using replicated caches used in a farm scenarios. This requires some support from the caching infrastructure, such as the ability to perform a hard lock on the values. Of the free caching providers for NHibernate, only Velocity support this, which means that when we evaluate a cache provider for NHibernate to be used, we need to take this into account.

In general, we can pretty much ignore this, but it does have some interesting implications with regards to what are the isolation guarantees that we can make based on the cache implementation that we use, the number of machines we use and the cache concurrency strategy that we use.

You can read about this here and here.

One thing that you should be aware of is that NHibernate currently doesn’t have transaction cache concurrency story, mostly because there is no cache provider that can give us that. As such, be aware that if you require serializable isolation level to work with your entities, you cannot use the 2nd level cache. The 2nd level cache currently guarantee only read committed (and almost guarantee repeatable read if this is the isolation level that you use in the database). Note that this guarantee is made for read-write cache concurrency mode only.

Comments

glade
04/24/2009 03:56 PM by
glade

Very timely post! We're in the middle of discovery on how we can leverage 2nd level cache.

Can you spend some time setting expecations when using the read-only concurrency model? We're looking to leverage our memcached instances with two load balanced app servers.

I setup two entities as read-only, one has a <bag of the other. When adding to the collection the ISoftLock .Lock() method is invoked and an exception occurs blocking writing to a read-only entity.

It seems to me the read-only model is incomplete, certainly with respect to <bag (i assume other collections, this was disheartening enough to stop me in my tracks.)

The only other conclusion I can reach is the read-only model is intended for read-only nhibernate usage.... not a read-only cache usage. But that conflicts with using mutable="false" to enable read-only cache for normal application usage...

glade
04/24/2009 03:59 PM by
glade

All those missing words are "bag", sucked up in the html soup.

Ayende Rahien
04/24/2009 05:14 PM by
Ayende Rahien

Glade,

read-only concurrency strategy is... read only.

It doesn't support writes.

Comments have been closed on this topic.