NHibernate Membase caching provider

Enyim has made a nice client library for both Memcache and Membase. Membase can be used as a free distributed caching platform.

With the new provider model introduced in ASP.NET 4, it is now possible to write providers for output caching and session state. Enyim has done just that with their memcache-provider.

I use NHibernate for some of my projects and being able to use a Membase cache provider for NHibernates second level cache would be very nice.

With help from Christian Dalager and my self, such a library is now available. Take a look here https://bitbucket.org/ovesen/membasecacheprovider

Zipped binaries are provided with mapping against NHibernate 2.0.1, 2.1.0, 2.1.2 and NH3 here https://bitbucket.org/ovesen/membasecacheprovider/downloads

An example configuration file is provided in the downloads, but else you can see the options here:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- This is a sample configuration file for using MembaseCacheProvider -->

    <configSections>

        <!-- (Required) Default configuration section for Membase -->
        <section name="membase" type="Membase.Configuration.MembaseClientSection, Membase" />

        <!-- (Optional) An additional configuration section for Membase, can be used to target another bucket or servers -->
        <section name="membaseNhibernate" type="Membase.Configuration.MembaseClientSection, Membase" />

        <!-- (Optional) A section for the Enyim Membase client, allows loggint to e.g. log4net -->
        <sectionGroup name="enyim.com">
            <section name="log" type="Enyim.Caching.Configuration.LoggerSection, Enyim.Caching" />
        </sectionGroup>

        <!-- (Optional, but recommended) A configuration section that allows specific settings to be set for different NHibernate caching regions -->
        <section name="membaseNhibernateCache" type="MembaseCacheProvider.MembaseNhConfiguration, MembaseCacheProvider" />

    </configSections>

    <!-- (Optional) Defines that log4net should be used for Membase client logging, remember to define log4net configuration -->
    <enyim.com>
        <log factory="Enyim.Caching.Log4NetFactory, Enyim.Caching.Log4NetAdapter" />
    </enyim.com>

    <!-- (Required) Maps to the configuration section named "membase", defines a single server url and the "default" bucket -->
    <!-- Other configuration settings are available, look in the Enyim documentation and examples -->
    <!-- here http://memcached.enyim.com/ or here https://github.com/enyim/memcached-providers -->
    <membase>
        <servers bucket="default">
            <add uri="http://localhost:8091/pools/default" />
        </servers>
    </membase>

    <!-- (Optional) Maps to the configuration section name "membaseNhibernate", defines a single server url and the bucket "default_nhibernate" -->
    <membaseNhibernate>
        <servers bucket="default_nhibernate">
            <add uri="http://localhost:8091/pools/default" />
        </servers>

        <!-- (Optional) Sets specific settings for the socket pool -->
        <socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" />

        <!-- (Optional) Sets a specific locator -->
        <locator type="Enyim.Caching.Memcached.DefaultNodeLocator, Enyim.Caching" />
    </membaseNhibernate>

    <!-- (Optional, but recommended) Configuration settings of NHibernate second level cache, Membase provider -->
    <!-- The section attribute defines that the Membase configuration names "membaseNhibernate" should be used -->
    <membaseNhibernateCache section="membaseNhibernate">

        <!-- Here expiration details are specified for the Nhibernate cache regions LongTerm and ShortTerm -->
        <!-- The expiration are defined in minutes, meaning LongTerm = 60 minutes and ShortTerm = 2 minutes -->
        <cache region="LongTerm" expiration="60" />
        <cache region="ShortTerm" expiration="2" />
    </membaseNhibernateCache>

</configuration>

21 thoughts on “NHibernate Membase caching provider”

  1. Hi,

    I am using some of your code along with the Membase server (memcache only), it appeared to be working fine however I am walking into some caching issues due to my application using multiple databases in a multi-tenant environment.

    It seems the cache is being shared between my 2 applications casuing entities to be returned from other tenants database if the ID matches, I thought NHibernate would create a seperate cache for each database by itself, do you know if there is any additional configuration that I need to perform?

    Thank you!
    Paul Hinett

  2. @Paul

    Yep, NHibernate multi-configuration would have this problem. A solution would depend on how you configure NHibernate to use different databases. Normally you would have connection string in the hibernate.cfg.xml, but how are your multi-configured NHibernate solution tied together?

    Best,
    Mikkel

  3. Each tenant uses a seperate session factory which are initialised on the application startup.

    Each tenant uses a single NHibernate xml config file, this config file doesn’t include the connection string though…i load the config file at runtime and add the connection string for each tenant which is pulled from the DB…here is some code snippets:

    var properties = new Dictionary
    {
    { “connection.connection_string”, tenant.ConnectionString },
    { “hibernate.search.default.indexBase”, tenant.Settings.LuceneIndexDirectory}
    };

    var cfg = NHibernateSession.AddConfiguration(factoryKey,
    mappingAssemblies,
    modelGenerator.Generate(),
    cfgFile,
    cfgProperties,
    null, null);

    Could I modify this to init the second level cache per tenant somehow.

    Thanks,
    Paul

  4. I asked this question on StackOverflow the other day too, someone mentioned that I would need to use different buckets per tenant which makes sense, but I have no idea how to specify the bucket name at runtime….as far as i can see i can only set it in the web.config section which is shared for all tenants.

  5. @Paul

    You can choose to use one bucket per tenant, however this means that you will have to configure each bucket with specific MB og space. Meaning, a tenant with a lot of traffic will need a large bucket, a tenant with less will need a smaller. But how will you determine the sizes?

    I would suggest another approach.

    When using this caching-provider, you have the option of specifying a KeyTransformer of type IMemcachedKeyTransformer in the configuration. I see that you, in the configuration, have access to a “tenant” object. You could add a CachePrefix property to this, and implement this KeyTransformer:

    public class TenantSpecificCacheKeyTransformer : IMemcachedKeyTransformer
    {
    public string Transform(string key)
    {
    key = tenant.CachePrefix + “_” + key;
    key = key.Replace(” “, “_”);
    return key;
    }
    }

    If the tenant.CachePrefix is unique, then you will eliminate key clashes. Do please remember to remove any spaces, as Membase will handle spaces in Keys wrongly.

    I hope this helps.

    Best,
    Mikkel

  6. Perfect that works great!

    Only issue I have is that I needed to use the ServiceLocator to get an instance of my Tenant object in the TenantSpecificCacheKeyTransformer default constructor.

    Other than that I now have a seperate cache per tenant.

    Paul

  7. @Paul

    Yes, unfortunately the solution is not very IoC friendly, but as long as it works.

    Maybe in a later release IoC will be better supported.

    :) Mikkel

  8. Mikkel,
    This looks like it could solve some of my issues with setting up Membase for NHibernate. What is the best way to configure this as a 2nd level cache? With the SysCache option, it looks like this:

    true
    true
    NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache

    Any tips on how to wire it into my NHibernate config? I presume I will still need the same config in my class mappings.

  9. @Brandon

    SysCache uses the built-in HttpRuntime cache, which is an in-memory cache. It is cleared on recycles and server reboots.

    My NHibernate config file has thease values:

    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class=MembaseCacheProvider.MemBaseCacheProvider, MembaseCacheProvider

    The mappings are all the same regardless of the caching provider.

    I hope this helps :)
    Mikkel

  10. @Aaron: I have not actually used the activerecord pattern for Nhibernate, as I like my classes to be slim and POCO.

    But Membase requires objects to be serializeable… actually I think NHibernate requires that.

    So, have you tried just plugging in Membase as a cache provider and see what happens?

  11. @Mikkel: i did try and it and it didn’t work. we’ve already implemented activerecord and we’re really comfortable with it. haven’t found any resources about it on the internet. i’m guessing the easiest to implement second level caching would be syscache, but i tried and i could still see all the queries showing up in the profiler so left it for now. it’d be great to figure out how to get any sort of second level caching to work for activerecord and nhibernate for .net4 with sql2008. i’m sure it’ll save so much resources.

  12. @Aaron: Note that by enabling second level caching does not automatically enable query caching. Query caching needs to be enabled for each query you perform. If you use the repository pattern, this should be done in each method of the repository. Take a look here. Another thing is, objects in the NHibernate cache are serialised, so all cacheable objects should be serialiseable.

  13. I have upgraded to Nhibernate 3.2 and having trouble with your provider.

    I rebuilt it agains NHibernate 3.2 library which compiled fine with no changes, however I have been getting random ‘Index out of range’ exceptions on some of my NHibernate queries.

    Is there anything I need to modify that you know about, or do you have an updated version to work with 3.2?

    Thank you.
    Paul

  14. @Paul I have not experienced any of your problems when upgrading, and looking at your exception I have no real suggestion on how to solve this. I have however made a new and updated library file ready for download. Both NHibernate and Enyim has been updated, I hope this can help solve your problem.

    Let me know how it goes.

  15. Hi Mikkel,

    The cache provider doesn’t provide any locking implementation. Now I’m assuming this is because MemBase handles this automatically, but I just wanted to confirm: Does this provider support multiple concurrent readers/writers for the same entity?

  16. Can you help me configure Fluent NHibernate+ NHibernate MemCache?

    private static ISessionFactory CreateNhSessionFactory()
    {
    var connStr = System.Configuration.ConfigurationManager.ConnectionStrings[“AESConnect”].ConnectionString;
    return Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connStr))
    .Cache(c=>c.ProviderClass(typeof(NHibernate.Caches.MemCache.MemCacheProvider).AssemblyQualifiedName).UseSecondLevelCache())
    .ExposeConfiguration(e=>e.SetProperty(“hibernate.cache.use_second_level_cache”,”true”))
    .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(HotelsMap))))
    .BuildSessionFactory();
    }

    This fails. I post similar question here. Please help.

    Thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>