nhibernate/NHibernate-Caches

How to use NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy

EngSayed opened this issue · 10 comments

According to the documentation

cache.strategy can take one of many values and one of them is NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy

When I try to configure it in the cache region configuration inside Web.config as following:
<cache region="community" strategy="NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy, NHibernate.Caches.StackExchangeRedis" />

Then I get the following exception:

NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy is not supported by NHibernate.Caches.StackExchangeRedis.DefaultCacheRegionStrategyFactory, register a custom NHibernate.Caches.StackExchangeRedis.ICacheRegionStrategyFactory or use a supported one.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: NHibernate.Cache.CacheException: NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy is not supported by NHibernate.Caches.StackExchangeRedis.DefaultCacheRegionStrategyFactory, register a custom NHibernate.Caches.StackExchangeRedis.ICacheRegionStrategyFactory or use a supported one.

[CacheException: NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy is not supported by NHibernate.Caches.StackExchangeRedis.DefaultCacheRegionStrategyFactory, register a custom NHibernate.Caches.StackExchangeRedis.ICacheRegionStrategyFactory or use a supported one.]
   NHibernate.Caches.StackExchangeRedis.DefaultCacheRegionStrategyFactory.Create(IConnectionMultiplexer connectionMultiplexer, RedisCacheRegionConfiguration configuration, IDictionary`2 properties) +305
   NHibernate.Caches.StackExchangeRedis.RedisCacheProvider.BuildCache(RedisCacheRegionConfiguration regionConfiguration, IDictionary`2 properties) +79
   NHibernate.Caches.StackExchangeRedis.RedisCacheProvider.BuildCache(String regionName, IDictionary`2 properties) +333
   NHibernate.Cache.CacheFactory.BuildCacheBase(String name, Settings settings, IDictionary`2 properties) +44

What am I doing wrong?

For some reason, it is not supported by the default factory. But you can easily provide your own by copying into your own code the one used in the project tests.

Yeah I found that factory inside tests and was wondering why it was not implemented directly inside the default factory

Also it is not mentioned in the documentation that we need to implement a custom factory to be able to use it.

@fredericDelaporte, after copying the factory, I am getting the following execption.

System.InvalidOperationException: Unknown type 'NHibernate.Caches.StackExchangeRedis.Messages.CacheKeyInvalidationMessage, NHibernate.Caches.StackExchangeRedis, Version=5.9.0.0, Culture=neutral, PublicKeyToken=6876f2ea66c9f443', use JsonCacheSerializer.RegisterType method to register it. at NHibernate.Caches.Util.JsonSerializer.JsonCacheSerializer.ExplicitSerializationBinder.BindToName(Type serializedType, String& assemblyName, String& typeName) at Newtonsoft.Json.Utilities.ReflectionUtils.GetFullyQualifiedTypeName(Type t, ISerializationBinder binder) at Newtonsoft.Json.Utilities.ReflectionUtils.GetTypeName(Type t, TypeNameAssemblyFormatHandling assemblyFormat, ISerializationBinder binder) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteTypeProperty(JsonWriter writer, Type type) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteObjectStart(JsonWriter writer, Object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) at NHibernate.Caches.Util.JsonSerializer.JsonCacheSerializer.Serialize(Object value) at NHibernate.Caches.StackExchangeRedis.TwoLayerCache.ExecuteOperation(String cacheKey, Object value, Boolean publish, Boolean remove) at NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy.ExecutePut(String cacheKey, Object value) at NHibernate.Caches.StackExchangeRedis.AbstractRegionStrategy.Put(Object key, Object value) at NHibernate.Caches.StackExchangeRedis.DefaultRegionStrategy.Put(Object key, Object value) at NHibernate.Caches.StackExchangeRedis.TwoLayerCacheRegionStrategy.ExecutePutMany(Object[] keys, Object[] values) at NHibernate.Caches.StackExchangeRedis.AbstractRegionStrategy.PutMany(Object[] keys, Object[] values) at NHibernate.Caches.StackExchangeRedis.DefaultRegionStrategy.PutMany(Object[] keys, Object[] values) at NHibernate.Cache.ReadWriteCache.PutMany(CacheKey[] keys, Object[] values, Int64 timestamp, Object[] versions, IComparer[] versionComparers, Boolean[] minimalPuts) at NHibernate.Cache.CachePutBatch.Execute(CachePutData[] data) at NHibernate.Cache.CacheBatcher.ExecuteBatch() at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder) at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder) at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder)

It looks like CacheKeyInvalidationMessage needs to be registered for NHibernate.Caches.Util.JsonSerializer.JsonCacheSerializer but not sure how and where?

That is something to do like that :

           var serializer = new JsonCacheSerializer();
           serializer.RegisterType(typeof(CacheKeyInvalidationMessage), "ckim");
           RedisCacheProvider.DefaultCacheConfiguration.Serializer = serializer;

var serializer = new JsonCacheSerializer();
serializer.RegisterType(typeof(CacheKeyInvalidationMessage), "ckim");
RedisCacheProvider.DefaultCacheConfiguration.Serializer = serializer;

It worked ... just a note:
I had to remove the configuration for "cache.serializer" as it was replacing that default configuration.

Looking again at this, the reason for not being supported by the default strategy factory is because these advances strategies require an additional memory cache, for which only a base abstract class is provided. The memory cache has to be implemented by the application.

So, its mainly a trouble of lacking documentation.

The documentation already states:

In order to use this strategy a custom ICacheRegionStrategyFactory has to be provided (see cache.region_strategy_factory setting), where the strategy is created with a custom RegionMemoryCacheBase implementation.

But it does so only for the last of the three advanced strategies using a local memory cache, while all of them have this same constraint.

You guys already did the implementation as we found but it is only inside the testing project. And all what I needed to do is to reference MemoryCache and fix that issue with seialization.
So it could be much more helpful if the documentation mentioned that this strategy needs a custom factory and also we can copy the same factory implementation from testing project.

Have a look at the linked PR. Is it not what we are doing?