MichaCo / CacheManager

CacheManager is an open source caching abstraction layer for .NET written in C#. It supports various cache providers and implements many advanced features.
http://cachemanager.michaco.net
Apache License 2.0
2.33k stars 458 forks source link

Multilayer cache with in memory and Redis backplane.... #290

Closed natiki closed 4 years ago

natiki commented 4 years ago
<configuration>
  <configSections>
    <section name="cacheManager" type="CacheManager.Core.Configuration.CacheManagerSection, CacheManager.Core"/>
    <section name="cacheManager.Redis" type="CacheManager.Redis.RedisConfigurationSection, CacheManager.StackExchange.Redis" />
  </configSections>

  <appSettings>
    <add key="CacheDefaultDurationMinutes" value="60"/>
    <add key="CacheKeyPrefix" value="Prefix_"/>
  </appSettings>

  <cacheManager.Redis xmlns="http://cachemanager.michaco.net/schemas/RedisCfg.xsd">
    <connections>
      <connection id="redisConnectionString" connectionString="redis.home.local:6379,allowAdmin=true,ssl=false" />
    </connections>
  </cacheManager.Redis>

  <cacheManager xmlns="http://cachemanager.michaco.net/schemas/CacheManagerCfg.xsd">
    <managers>
      <cache name="cache" updateMode="Up" enableStatistics="false" enablePerformanceCounters="false">
        <handle name="Memory" ref="SystemRuntimeHandle"/>
        <handle name="redisConnectionString" ref="redisSliding" isBackplaneSource="true" />
      </cache>
    </managers>
    <cacheHandles>
      <handleDef id="SystemRuntimeHandle" type="CacheManager.SystemRuntimeCaching.MemoryCacheHandle`1, CacheManager.SystemRuntimeCaching"/>
      <handleDef  id="redisSliding" type="CacheManager.Redis.RedisCacheHandle`1, CacheManager.StackExchange.Redis" defaultExpirationMode="Sliding" defaultTimeout="5m" />
    </cacheHandles>
  </cacheManager>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>
</configuration>

What I am trying to achieve is have an in memory copy in front of the Redis back plane. Any changes to a LOCAL copy in any of the connected clients is to make its way to the local copy of the other clients.

While I can see the information making its way into Redis, however if client A is REMOVE or PUT of any key it is not reflected in the local copy of Client B. ie Client B always only sees its local copy changes.

What am I missing here?

MichaCo commented 4 years ago

The local cache gets populated only as needed, the backplane only helps to have less stale data in the local cache layer. Any changes in other instances will simply remove the key from local cache.

If client A creates or changes a key, the data will only be in Redis. Client B will receive and cache the changes locally only after the first time you Get them again.

natiki commented 4 years ago

Client B will receive and cache the changes locally only after the first time you Get them again.

So this means only once it has expired from local cache? Because until then it will ultimately keep getting them from its local cache?

So the only way to kind of achieve what I want is to:

a) Set a low fixed expiry in the memory layer - so that when it expires it will get a fresh copy from the second level aka Redis? b) If I want them expired more timeously then I would need a messaging layer that each of the clients subscribe to expire them from the local cache (immediately) but that then means it would propagate upwards and remove them in Redis as well? Or is there another way?

MichaCo commented 4 years ago

No, I think you misunderstood what I said.

If you use the CM backplane, that will remove items which either changed or got removed from Redis in all local cache instances.

Let's say you use the cache in an app where a key exists only in Redis and you do a Get, that key will be cached locally now because you did execute a Get. Now, if that key gets modified by another instance, CacheManager will remove it from local cache (via Backplane pub sub messages) so that the next time you do a Get, you get a fresh copy from Redis instead of the stale local copy.

natiki commented 4 years ago

OK so at the moment I am still getting the stale local copy until it is removed based on timeout. Is it because I am using Put instead of Update that I am seeing this behaviour?

I attach my simple test harness that shows the issue. Run up two copies.....

Instance A: Click Set -- And Key = Simpson Instance B: Click Get -- And Key = Simpson Instance A: Change value to SimpsonX Click Set -- And Key = SimpsonX Instance B: Click Get -- And is still Key = Simpson Instance B: Wait until OnRemoveByHand event fires and then click Get() and finally value will be SimpsonX

wfTestHarness.zip

MichaCo commented 4 years ago

Yeah you are not using the backplane, without that, that's the expected behavior

I should have seen that earlier but your app.config doesn't enable the backplane feature, see http://cachemanager.michaco.net/documentation/CacheManagerCacheSynchronization for an example config you are missing the backplaneType on the cache section I guess.

Haven't used the xml configuration in years.