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!

Article series on NHibernate and Fluent NHibernate – Part 2