CacheAdapter 3.0 Released
I am happy to announce that CacheAdapter Version 3.0 has been released. You can grab the nuget package from here or you can download the source code from here
For those not familiar with what the CacheAdapter is, you can read my past posts here, here, here, here and here but basically, you get nice consistent API around multiple caching engines. Currently CacheAdapter supports in memory cache, ASP.Net web cache, memcached, and Windows Azure Appfabric cache. You get to a program against a clean easy to use API and can choose your caching mechanism using simple configuration. Change between using ASP.Net web cache to a distributed cache such as memcached or Appfabric with no code change,just some config.
Changes in Version 3.0
This latest version incorporates one new major feature, a much requested API addition and some changes to configuration.
Cache Dependencies
CacheAdapter now supports the concept of cache dependencies. This currently is rudimentary support for invalidating other cache items automatically, when you invalidate a cache item that is linked to the other items. That is, you can specify that one cache item is dependent on other cache items. When a cache item is invalidated, their dependencies are automatically invalidated for you. The diagram below illustrates this. In the scenario below, when ‘ParentCacheKey’ is invalidated/cleared, then all its dependent items are also removed for you. If only ‘ChildItem1’ was invalidated, then only ‘SubChild1’ and ‘SubChild2’ would be invalidated for you.
This is supported across all cache mechanisms and will include any cache mechanisms subsequently added to the supported list of cache engines.Later in this blog post (see ‘Details of Cache Dependency features’ below), I will detail how to accomplish that using the CacheAdpter API.
Clearing the Cache
Many users have asked for a way to programmatically clear the cache. There is now a ‘ClearAll’ API method on the ICacheAdapter interface which will do just that. Please note that Windows Azure Appfabric cache does not support clearing the cache. This may change in the future however the current implementation will attempt to iterate over the regions and clear each region in the cache but will catch any exceptions during this phase, which Windows Azure Appfabric caching will throw. Local versions of Appfabric should work fine.
Cache Feature Detection - ICacheFeatureSupport
A new interface is available on the ICacheProvider called ICacheFeatureSupport which is accessed via the FeatureSupport property. This is fairly rudimentary for now but provides an indicator as to whether the cache engine supports clearing the cache. This means you can take alternative actions based on certain features.
As an example:
if (cacheProvider.FeatureSupport.SupportsClearingCacheContents())
{// Do something
}
Configuration Changes
I felt it was time to clean up the way the CacheAdapter looks for its configuration settings. I wasn’t entirely happy the way it currently works so now the CacheAdapter supports looking for all its configuration values in the <appSettings> section of your configuration file. All the current configuration keys are still there but if using the <appSettings> approach, you simply prefix the key names with “Cache.”. An example is probably best. Previously you used the configuration section to define configuration values as shown here:
<applicationSettings><Glav.CacheAdapter.MainConfig><setting name="CacheToUse" serializeAs="String"><value>memory</value></setting><setting name="IsCacheEnabled" serializeAs="String"><value>True</value></setting><setting name="IsCacheDependencyManagementEnabled" serializeAs="String"><value>False</value></setting></Glav.CacheAdapter.MainConfig></applicationSettings>
Now you can achieve the same effect using <appSettings> element as shown here:
<appSettings><!--memory-->
<add key="Cache.CacheToUse" value="memory"/><add key="Cache.IsCacheEnabled" value="true"/><add key="Cache.IsCacheDependencyManagementEnabled" value="true" /></appSettings>
This approach makes it easier to integrate into your current solutions and a little bit cleaner (IMHO). If you specify configuration values of the same key in both the configuration section *and* the <appSettings> section, then the <appSettings> values will take precedence and override the values in configuration section. You do not need the configuration section anymore.
Support new configuration values for Windows Azure Caching (& Appfabric)
ChannelOpenTimeout
The CacheAdapter now supports us of the ChannelOpenTimeout value in the CacheSpecificData section of config for Windows Azure Appfabric caching. This can be useful for debugging scenarios where you need to allow a much longer connection window so you can see the actual error being returned from the host, as opposed to the client forcibly disconnecting before the error is returned. This value is set in seconds. For example, to set this value to 2 minutes, simply use:
<add key="Cache.CacheSpecificData" value="UseSsl=false;ChannelOpenTimeout=120;SecurityMode=Message;MessageSecurityAuthorizationInfo={your_security_token}"/>
MaxConnectionsToServer
The CacheAdapter now supports us of the MaxConnectionsToServer value in the CacheSpecificData section of config for Windows Azure Appfabric caching. This allows fine tuning performance for the number of concurrent connections to the cache service. Currently, the Azure client defaults to 1.
Details and usage of Cache Dependency features
The major feature introduced in this release is the cache dependency support. Not all cache implementations support this natively (like memcached for example) but generic support has been added across all current and future cache mechanisms. This is performed through the use of a GenericDependencyManager class. This class uses simplistic techniques to achieve dependency management. No cache specific features are employed which is why it works across all implementations of ICache, the generic cache interface that abstracts each cache mechanism. Later releases will include specific classes that implement dependency management in specific ways for each cache engine that support it. Appfabric, for example, has some dependency and notification support built into its API which can be used.
Enough chatter, lets see how its used.
The following code retrieves some data from the cache using a key of “Bit1”. If not present in the cache, it is added. The interesting part is the very last argument, "MasterKey”. This argument specifies that this cache item with a key of “Bit1” is dependent on “MasterKey”.
var bitOfRelatedData1 = cacheProvider.Get<string>("Bit1", DateTime.Now.AddDays(1), () => "Some Bit Of Data1","MasterKey");
When “MasterKey” is invalidated from the cache using the API call
‘InvalidateCacheItem(string key)’
then the cache item with key “Bit1” will also be invalidated. Thats pretty much it.
it should be noted that the ‘ParentKey’ or ‘MasterKey’ specified as the parent dependency does not actually have to be an item in the cache. It can be simply any arbitrary name. Simply call InvalidateCacheItem for that parent key and all dependencies are also invalidated.
The CacheProvider exposes a new interface which is used to manage all cache dependencies. This interface is the ICacheDependencyManager and is exposed as the InnerDependencyManager property on the ICacheProvider instance.
This interface has the following methods which allow low level implementation of the CacheDependency definitions and perform actions against those dependencies.
public interface ICacheDependencyManager{void RegisterParentDependencyDefinition(string parentKey, CacheDependencyAction actionToPerform = CacheDependencyAction.ClearDependentItems);void RemoveParentDependencyDefinition(string parentKey);void AssociateDependentKeysToParent(string parentKey, IEnumerable<string> dependentCacheKeys, CacheDependencyAction actionToPerform = CacheDependencyAction.ClearDependentItems);IEnumerable<DependencyItem> GetDependentCacheKeysForParent(string parentKey, bool includeParentNode = false);string Name { get; }void PerformActionForDependenciesAssociatedWithParent(string parentKey);void ForceActionForDependenciesAssociatedWithParent(string parentKey, CacheDependencyAction forcedAction);bool IsOkToActOnDependencyKeysForParent(string parentKey);}
As mentioned previously, the GenericDependencyManager implementation that is packaged with this version of the CacheAdapter uses no specific cache features to manage dependencies and so works with all cache types. In the future, more specific implementation will be provided to utilise cache specific features to make dependency management more efficient.
Finally, you will notice an action enumeration ( CacheDependencyAction ) that can be associated with dependencies when adding items to the cache which suggest that there is support for raising events or invoking callbacks when a cache item is invalidated. Currently there is no functionality to support this but future revisions may include these depending on the features of the cache used.
And that’s a wrap
I hope these features provide useful for people. I welcome all feedback and also try to get around to managing all pull requests (yes I am pretty slow at this).
Enjoy.