NHibernate Caching: The Secong Level Cache Space Is Shared!

Let us assume that you have an architecture that demands that you'll have a database per client (quite common, by my experiance), but the application layers are the same. This is usually done for reasons of security and/or scalability. So there is no way a client can get information for another client.

This is fairly simple to handle with NHibernate, you just create a session factory per each client. This is actually an advantage, since you can start playing with the implementations of the classes for each client. (Yes, you are reading me correctly. In this case, it possible to turn NHibernate into an Inversion Of Control container).

The issue turns up when you have caching turned on. Assuming you have an entity of the same type with the same identifier in two of the databases, you may very will mix the data when it is cached. NHibernate maintain a single second level cache per session factory, so you are supposed to be safe from this. The problem is that the cache space is shared (and it has to be shared).

Let us take an example of an Employee object with id of #5, which exist in both IniTech and Fuzbar databases. When the employee instnace is loaded and put in the cache, the cache key that is used looks something like: "NHibernate:Entities.EmployeeManagement.Employee:5@5". Now, when we try to load an employee from the second database, we first lookup in the cache, finding that there is an instance there, because the cache keys matches.

This is happening because the cache space (so to speak) is the same space for all the caching strategies. For SysCache, it is the ASP.Net cache that is used for the cache space. For distributed caching, Memcached is used. In both cases, if the same cache key is generated, the information will be returned even if the key was actually inserted by another session factory.

In this sceanrio, I solved the issue by modifying the SysCacheProvider to be aware of the current database, and insert the current database name to the cache key. This solved all the issues in one stroke.

The JIRA issue for this is here. I was surprised to learn that I was the first to encounter this, and in the comments to the JIRA issue, I found that indeed I wasn't the first. :-)

The issue is solve by specifying cache region prefix in the session factory configuration, but the functionality wasn't ported to NHibernate yet.

Print | posted on Friday, September 29, 2006 3:18 PM

Feedback


Gravatar

#  9/29/2006 6:36 PM Kevin Williams

As you probably know, I've written all the (free) NHibernate second-level cache implementations to date. Therefore, there should be no surprise that the implementations are roughly the same. :) I'm glad someone finally is using caching is scenarios I never thought of. This is what we need to bring NHibernate to the top of the heap of .NET ORM. Thanks for digging so deep!

Have you tried the Memcached version? I'm curious to see how it works for someone in the real world.

Cheers!


Gravatar

#  9/29/2006 7:18 PM Ayende Rahien

I'm going to do some stuff with the Memcached cache on Thursday, actually. :-)
Are there any _non_ free caching implementation for NHibernate?
Even in this project, we will likely move to Memcached at some stage (we are talking about quite a bit of data, and the DB is a scarce resource).

By the way, what driven you to write the Prevelance implementation? The way I understand it, it is more of a competitor to NHibernate (in that it handles object persistence) than a caching mechanism.


Gravatar

#  9/29/2006 7:46 PM Kevin Williams

http://www.alachisoft.com/ncache/index.html

I inquired about the price once, and the quote was around $10,000. I almost had a heart attack.

I hope the memcached cache can fill the need for a distributed 2nd-level cache, because NCache is rediculously expensive.

I don't remember why I wrote the Prevalence cache, that was years ago. It was the first one I wrote (not including the failed port of EHCache), and it stinks. I wish it would just go away.

I've tried to make a distributed cache out of db4o (http://www.bantamtech.com/solutions/byname/zcache) but that seems to not perform well either. I'm about to give up on the whole caching thing - my code sucks.


Gravatar

#  9/30/2006 9:02 PM Bill Pierce

A good use case for using Guids as primary keys.


Gravatar

#  9/30/2006 9:20 PM Ayende Rahien

Actually, no there are a number of reasons to use GUIDs as PK, but this isn't one of them.
This is a bug (which will be shortly fixed) in the implementation of 2nd level caching of NHibernate.
I don't think that bugs should drive my design.

Comments have been closed on this topic.