NHibernate 2nd level cache and multiple databases

Today we had an issue with our system. The application was showing unexpected behavior when different people where working with different databases. The application is a Silverlight based application. What we found out was that although the application was using different NHibernate session factories all of these factories used the same 2nd level cache. That was a total surprise to us since the documentation of NHibernate clearly states that the 2nd level cache is tied to a session factory.

A quick chat with Fabio Maulo brought light into the dark. NHibernate indeed assigns a different 2nd level cache instance to each session factory. But it depends on the 2nd level cache provider what happens in reality!

In our application we use the ASP.NET cache (NHibernate.Caches.SysCache). The provider for this cache is part of the NHibernate contributions. A quick analysis of the code showed that the provider does NOT automatically differentiate how the content is stored in the cache when originating from or targeting different databases (that is different session factories).

Fortunately we quickly found a solution by using an undocumented configuration property called “regionPrefix”. We now just configure each of our session factories with a different regionPrefix and as a result the 2nd level cache is working as expected.

We use a function similar to this to get the necessary properties needed by the NHibernate configuration to build a session factory. Each factory has a distinct region prefix and connection string.

   1: private static IDictionary<string, string> GetNHConfigProperties(string regionPrefix, string connectionString)
   2: {
   3:     IDictionary<string, string> props = new Dictionary<string, string>();
   5:     props.Add("connection.connection_string", connectionString);
   6:     props.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
   7:     props.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");
   8:     props.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
   9:     props.Add("cache.provider_class", "NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache");
  10:     props.Add("cache.use_second_level_cache", "true");
  11:     props.Add("cache.use_query_cache", "true");
  12:     props.Add("show_sql", "true");
  13:     props.Add("expiration", "180");
  14:     props.Add("regionPrefix", regionPrefix);
  15:     props.Add("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
  17:     return props;
  18: }

Note the definition of regionPrefix on line 14. We assign a value that is provided as a parameter to the function like the connection string.

Once again the NHibernate profiler was an invaluable help for us when monitoring the behavior of the system!

About Gabriel Schenker

Gabriel N. Schenker started his career as a physicist. Following his passion and interest in stars and the universe he chose to write his Ph.D. thesis in astrophysics. Soon after this he dedicated all his time to his second passion, writing and architecting software. Gabriel has since been working for over 25 years as a consultant, software architect, trainer, and mentor mainly on the .NET platform. He is currently working as senior software architect at Alien Vault in Austin, Texas. Gabriel is passionate about software development and tries to make the life of developers easier by providing guidelines and frameworks to reduce friction in the software development process. Gabriel is married and father of four children and during his spare time likes hiking in the mountains, cooking and reading.
This entry was posted in NHibernate. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • DaRage

    This is clearly a bug and not a configuration issue. This configuration parameter should not have been exposed in the first place and it is the session factory responsibility to guarantee independence from other session factories. after that is done then you can implement a feature that make 2 session factories use the same cache just like you have 2 applications with the same encryption keys. but that’s another story. The default behavior should differentiate between session factories. This definitely needs to be submitted as an issue.

  • @DaRaga: that’s what I was expecting too, that the cache provider should automatically detect that it is called from 2 different session factories. Maybe I should test the other providers too and compare their behavior…

  • Steve

    NHibernate is very famous among the developer’s community as it has a lot of advantages for them. But it cannot handle ASP.NET worker process recycling. So the use of cache providers like NCache or Velocity can be a good option. NCache acts like a second level distributed cache which can enhance the performance of the app.

  • jerry

    all have written the same. write something for beginers.

  • Joe

    here is a good read about how a distributed cache can be used as NHinerbate L2 cache,