Appfabric Cache: Read from & Write to Database (Read through - Write behind)

The Windows Server Appfabric 1.1 CTP has the much awaited Read-through and Write-through feature.

  • Read through - If a requested item doesn't exist in the cache, the data can be loaded from backend store and then inserted into cache. With a read-through provider, the cache detects the missing item and calls the provider to perform the data load. The item is then seamlessly returned to the cache client.  
  • Write behind - In the same way, items that are added or updated in the cache can be periodically written to the backend store using a write-through provider. This happens asynchronously and on an interval defined by the cache.

One key scnarios where this feature is handy is when a client requests for an item that has already been removed from cache. The item will automatically be loaded back to cache from the backend store. Likewise the changes you do to the items in cache are persisted asynchronously from time to time without any user intervention.

To accomplish this feature, you will have to write a provider (class library) that extends DataCacheStoreProvider abstract class. Then you need to install (in gac) your provider to each of the cache host of the cache cluster. The final step is to configure your named cache to use this provider. To get started make sure you have installed the Windows Server Appfabric 1.1 CTP. Next, create  a C# class library and reference the library Microsoft.ApplicationServer.Caching.  For 1.1 CTP, this is normally located under 'Program Files/Windows Server Appfabric'. Now implement the abstract class DataCacheStoreProvider.

 using Microsoft.ApplicationServer.Caching;
 
 namespace SampleProvider
 {
 public class Provider:DataCacheStoreProvider
 {
 
 private String dataCacheName;
 private String connectionString;
 
 public Provider(string cacheName, Dictionary<string, string> config)
 {
 dataCacheName = cacheName; //Store the cache name for future use
 connectionString = config[0].Value;
 }
 
 public override void Delete(System.Collections.ObjectModel.Collection<DataCacheItemKey> keys){} 
 
 public override void Delete(DataCacheItemKey key){} 
 
 protected override void Dispose(bool disposing){} 
 
 public override void Read(System.Collections.ObjectModel.ReadOnlyCollection<DataCacheItemKey> keys, IDictionary<DataCacheItemKey, DataCacheItem> items){} 
 
 public override DataCacheItem Read(DataCacheItemKey key){} 
 
 public override void Write(IDictionary<DataCacheItemKey, DataCacheItem> items){} 
 
 public override void Write(DataCacheItem item){} 
 }
 }
 A few things about the above code first. Your provider needs to have a constructor with the following signature. These parameters are passed in when your cache host initializes. The cacheName would contain the name of the cache for which you have created the provider. 
 The config will contain any configuration details like the connection string that you can pass while registerting your provider in the final step. public Provider(String cacheName, Dictionary<string, string> config) The other methods you need to implement are the Read, Write, Delete and Dispose. 
 You need not implement all the methods. If you want only read-through feature, just implement the Read. The following table gives you a gist of what each of the function does. 
Read Read a value from database, load it into the cache as well as return it to the client requesting for the cache item.
Write Persist items in the cache to the backend store (database) after a specified amount of time.
Delete Delete an item from the backend store when a cache item is removed from the cache by the client.
Dispose Called when the host is shutting down. Any cleanup code goes here.

 

 

 

Implementing Read

Read has two methods. One which takes a dictionary collection and the other takes a DataCacheItemKey. Read requests are always sent as a dictionary collection. Let us first implement the function with DataCacheItemkey parameter and then use it to process the other read function which accepts the dictionary collection.

  public override DataCacheItem Read(DataCacheItemKey key)
 {
 Object retrievedValue = null;
 DataCacheItem cacheItem;
 
 retrievedValue = ReadFromDatabase(key.Key); //Your implemented method that searches in the backend store based
 
 if (retrievedValue == null)
 cacheItem = null;
 else
 cacheItem = DataCacheItemFactory.GetCacheItem(key, dataCacheName, retrievedValue, null);
 return cacheItem; 
 }

You need to use DataCacheItemFactory class to create your cache item as follows DataCacheItemFactory.GetCacheItem(key, dataCacheName, retrievedValue, null) . If the item is not found, we are returning null which matches with the cache cluster behaviour. Next lets implemet the read which accepts the dictonary collection.

  public override void Read(System.Collections.ObjectModel.ReadOnlyCollection<DataCacheItemKey> keys, IDictionary<DataCacheItemKey, DataCacheItem> items)
 {
 foreach (var key in keys)
 {
 items[key] = Read(key);
 }
 }

As mentioned before, we are just using the 'public override DataCacheItem Read(DataCacheItemKey key)' to read the items from backend store. You need to add the items to IDictionary<DataCacheItemKey, DataCacheItem> items as in the code above before returning from read method.

 

Implementing Write

Write is implemented the same way as read. It's important to remove the items which has been written to the backend store from the IDictionary<DataCacheItemKey, DataCacheItem> items. If you have successfully written all the values to the backend store, you can simply clear the items dictionary object by calling items.Clear().

  public override void Write(IDictionary<DataCacheItemKey, DataCacheItem> items)

 

Compiling your provider

Your provider should be signed with a stong key. To do this, go to your Project Properties> Signing > Check 'Sign the assembly'> Choose or create a strong key name file.

 

Installing your provider

Your provider should be placed in the GAC of all the hosts of the cluster. Run the visual studio command prompt and type in the following command to add your provider to gac.

gacutil /i SampleProvider.dll

You now need to get the fully qualified name of your provider. To do this type the command without the dll extension

gacutil /l SampleProvider

This would give you a message as follows:

SampleProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0dca281230246e10, processorArchitecture=MSIL

We need to add the class details to the above message and remove the processorArchitecture. We will be using this string to register your provider with the cache 

SampleProvider.Provider, SampleProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0dca281230246e10

Registering your provider

Time to fire up the cache administration powershell. Here I am registering a provider for an existing named cache. If you are creating a new cache, you need to use the same parameters with the new-cache command.

Set-CacheConfig TestCache -ReadThroughEnabled true -WriteBehindEnabled true -WriteBehindInterval 60 -ProviderType "SampleProvider.Provider, SampleProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0dca281230246e10" -ProviderSettings @{"DbConnection"="<your connection string>";}

If your provider supports read through, set -ReadThroughEnabled to true. If your provider supports write behind, set -WriteBehindEnabled to true and also the interval between which cache items are persisted to database store. The minimum duration that can be set in this CTP is 60 seconds. In the -ProviderType, pass the string that we created in the step "Installing your provider". To pass config parameters to the initializer 'public Provider(string cacheName, Dictionary<string, string> config) ', make use of -ProviderSettings as in example above. Make sure you have set the same config for all the hosts in your cluster. Once that is done, start your cache cluster. If any of your cache host fails to start, probably there is some problem with your provider. Look for errors in your system event log.

 

Testing your provider

To test your provider, create a sample test client and request for an item that is not present in cache. The read of your provider will be called when the cluster fails to find the item in cache. To test write, add an item to cache cluster and wait for the -WriteBehindTimeInterval to pass.

 

Debugging your provider

To debug your provider code, open the class project and then go to Debug>Attach to process> and select DistributedCacheService.exe. Put debug points as necessary. Read will be hit when you request for a key.

 

Feel free to send in your comments or questions. Happy coding. :)