Windows Server AppFabric 1.1 Caching – Data Cache Store Provider

Windows Server AppFabric version 1.1 has worthy improvements over its predecessor version. The first from my v1.1 favorites list is the ability to retrieve data from data sources when the client-requested data is not available in the cache ("cache miss") and also save modified cache data back to the data source – a complete round-trip (Microsoft calls this "read-through & write-behind"). My second one is the option to compress the cached data exchanged between the cache client and the cache server thus improving the network performance.

 

Similar to WCF, AppFabric 1.1 supports allows multiple cache configuration sections on the client side and choose which one to use in the code. If not chosen programmatically, the section named "default" is used automatically when the cache client starts. This latter is useful when testing cache clients in multiple environments like DEV, TESTING, STAGING and PROD since it requires just a single configuration change. Here is a sample client-side cache configuration section:

Multiple Client Cache Config Sections

Load Cache Client Config Using Code

Without the above code, cache client will load the default section. If no default cache configuration section is specified and your code doesn’t explicitly load a named section either, a runtime error will occur when you use the data cache factory.

Alright, back to the main topic of this post – data cache store provider. AppFabric 1.0 let you rely only on "cache-aside" programming model to handle any cache-miss scenarios. That is, your application is responsible for loading data from the data store (and store it in cache) if that data is not found in the cache. With v1.1, you can let AppFabric itself "fetch" the data from the data source whenever "cache miss" occurs, keep the fetched data in the cache and send it back to the client. Of course, we should fill the "how" part of the fetching process. Turning 180°, AppFabric v1.1 also makes it possible to save updated cache data (if happens) back to the data store. Like the read process, we have to plug in the "how" part of the persistence process.

AppFabric exposes read-ahead and write-behind mechanisms using familiar Provider design pattern. Implement the abstract class Microsoft.ApplicationServer.Caching.DataCacheStoreProvider in your assembly and hook it up with AppFabric cache. Note the following:

  • Data cache store provider implementation assembly and its dependents should be deployed to GAC (strong naming implied)
  • Provider is attached to a cache at the time of cache creation (New-Cache) or later (Set-CacheConfig)
  • Provider class exposes a public constructor with two arguments: a string for the cache name and a Dictionary<string, string> for provider-specific parameters

Let’s see a sample data cache store provider using NorthWind database:

Provider Sample Part 1

AppFabric invokes the constructor in two occasions: (1) when the cache-cluster starts (2) when you attach the provider with a cache using either New-Cache or Set-CacheConfig. The Delete method is invoked when you remove a cache item from the cache (DataCache.Remove()).

Now the crucial part: the read and write methods. Let’s implement the data store read methods first. Note that there are two overloaded read methods:

Provider Sample Part 2

As said earlier, the reads methods are invoked when a cache item with requested key is not found in the cache (from my experiment so far, AppFabric usually calls the first overload). In order to make both methods behave consistently, the collection overload of read method internally calls the simple read method. A null return value from the simple read throws an exception on the cache-client! Let us complete the write methods as well.

Provider Sample Part 3

Write methods are invoked when cached items are updated by cache clients (for example, calling DataCache.Put()). Unlike read, write and delete methods are not immediately called when cache items are updated or removed. Rather, AppFabric calls them in (configurable) regular intervals.

The final piece the Dispose() method:

Provider Sample Part 4

Pretty straightforward! :-)

Assuming the provider assembly builds successfully and provided a strong name, you can associate it with a new cache as below (you can also use Set-CacheConfig cmdlet to enable or disable the provider for a cache):

New-Cache "ProviderEnabledCache" -ReadThroughEnabled true -WriteBehindEnabled true -WriteBehindInterval 60 -ProviderType "MsSqlDataCacheStoreProvider.CoreDataCacheProvider, CoreDataCacheStoreProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=21b666fac19955ad" -ProviderSettings @{"conStr"="Data Source=(local)\SQLEXPRESS;Initial Catalog=Northwind;Trusted_Connection=True"}

If everything went ok, the new cache should have been created with a data cache store provider enabled and attached to it. The most common reason for failed provider configuration is that one or more dependent assemblies (other than .NET framework assemblies) of the provider assembly missing in the cache host’s GAC. Needless to say, you have to deploy this provider assembly (including its dependents) on all cache hosts.

Points to note:

  • When the GAC is updated with a newer provider assembly, you have to restart the cache cluster for the new bit to get effect.
  • You do not have to implement both read and write methods. For example, if your cache has only read-through option enabled, you may just have empty write methods. Similarly, when you have only write-behind enabled, your read methods can be placeholder implementations.
  • Cache clients are not notified of uncaught exceptions thrown from write methods.
  • Provider class should have a public instance constructor that accepts a string and Dictionary<string, string> as parameters. Otherwise, the cache cluster will not start or will render unpredictable behavior.