Effective Cache Expirations
page 1 of 1
Published: 07 Oct 2003
Unedited - Community Contributed
Abstract
Receive notification when a cached item is removed from the cache and effectively manage the repopulation of the data.
by Robert Boedigheimer
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 45949/ 107

Background

The ASP.Net Cache object is extrememly useful for storing information that can be shared for all users of a web site. It is similar to the Application object, but provides the ability to set expiration criteria and dependency information. The expiration criteria includes a specific date and time, or a sliding time period. The dependency information can cause an expiration based on when a file, such as an XML file, is changed. These are all extremely useful for specifying when the cache should no longer be considered valid. There are two general approaches for how to handle cache entries that are no longer up to date. One approach is to just allow the entry to expire and to wait until it is required again to update it. An alternative approach is to be notified by ASP.Net when an item expires, and to immediately repopulate the cache item so that when a user needs the data it is immediately available.

This article will focus on how to capture cache expirations and repopulate the cache. The key to using this technique is to immediately replace the item that just expired with its original value, then spend time acquiring the updated data, and if successful replace the outdated data. There are serveral reasons why this is useful:

  • By replacing the data immediately with the value that just expired, users can continue to access the old information while the new data is being acquired rather than having to wait.
  • If there is an error while attempting to update the data, the cache will not be empty but will simply contain possibly old data. For example, if the web service or database required to get the latest data is not available it is often better to use potentially stale data than to have no data at all.
  • Using a expiration notification avoids race conditions when the cache expires since only a single method will be called to attempt to udpate the data (see On the Hour Caching) rather than each user that requests a page that requires the data.

Technique

The first step in using this technique is to create a method that will be called when the cache item is removed from the cache. This involves a delegate, which can be described as a "type safe function pointer" for .Net, which just means it is a way to indicate a given method that should be invoked. It is important to consider where this method should be located. When a user requests a .aspx page, an instance of a class that derives from the System.Web.UI.Page is created, its events are invoked and the HTML response is created, then after the Page_Unload() event fires the instance is destroyed. Since page object instances are only alive during specific page requests, where would you place a method to be notified by ASP.Net when a cache item expires? It certainly could be placed in the code behind file for a particular page, but that unfortunately requires that the given instance of the derived Page class must be kept in memory so that method will be available when needed. This can be very expensive if the object is large. Rather than waste memory, create a separate class file that will be used exclusively for the method.

Delegate callback class

Create a C# class file and include the following code:

public class cacheExpire
{
    //This holds a reference to the method to call back
    private CacheItemRemovedCallback OnRemove = null;

    public cacheExpire()
    {
      OnRemove += new CacheItemRemovedCallback(CacheDumped);
    }

    public void AddCacheItem(int i, int iMinutes)
    {
      //Add to the cache with key value "A"
      System.Web.HttpRuntime.Cache.Insert("A", i, null, 
         DateTime.Now.AddMinutes(iMinutes), 
         System.Web.Caching.Cache.NoSlidingExpiration, 
         CacheItemPriority.Normal, OnRemove);    
    }

    public void CacheDumped(string k, object v, CacheItemRemovedReason r)
    {
      if ((r == CacheItemRemovedReason.Removed) || 
        (r == CacheItemRemovedReason.Underused))
      {
        return;
      }

     //Put the item back, but use smaller expiration 
     AddCacheItem((int)v, 2);
     System.Threading.Thread.Sleep(5000);

     //Put the new data in for original time frame
     AddCacheItem(3, 5);    
    }
}

The CacheDumped() method is the key to effective management of this cache item. The variable k contains the key value of which item was removed from the cache. The variable v contains the item that was just removed from the cache. The variable r contains the reason why the item was removed from cache. There are four reasons why it may be removed:

  • DependencyChanged - The item is removed from the cache because a file or key dependency changed.
  • Expired - The item is removed from the cache because it expired.
  • Removed - The item is removed from the cache by a Remove method call or by an Insert method call that specified the same key.
  • Underused - The item is removed from the cache because the system removed it to free memory.

The Removed reason is certainly one in which we do not want to repopulate the cache because it was either specifically removed or a new value was already placed in the cache. The Underused indicates that there was limited memory and this was one of the least used items, so it also probably does not pay to update the value. The other two cases are those in which the data should now be considered possibly stale and should be handled. At the moment that this method is called, any attempt to access the cache item with the given key will return null. In order to avoid access to the cache from returning null while the new data is acquired, this function immediately places the original data back into the cache. The sample simulates a call to a web service or database by simply sleeping for 5 seconds, actual sites would place the necessary code here to get the latest data. If the cached item was using a dependency on an XML file, this would probably mean accessing the new file to get the data. It would be important to setup exception handling to ensure if an problem occurs it is handled properly. The advantage of this technique is that the original data was already placed back in the cache, so if the update fails the site can still access some data rather than have no data at all. It is very important to realize that this method does NOT execute in the context of a page request, so it is not possible to use the Response object or provide information to a user interface if problems occur, one solution is to write information to the event log.

The AddCacheItem() was just created to be a central function that would implement the caching for this item since it is called in multiple places.

Setup of cache item for expiration

The setup of the cache can be done in many different ways, here is a sample page that is created just to populate the cache. The page simply creates an instance of the CacheExpire class and calls a method to add the item to the cache:

private void Page_Load(object sender, System.EventArgs e)
{
    cacheExpire oExpire = new cacheExpire();
   
    //This adds the value 1 to the cache for 5 minutes
    oExpire.AddCacheItem(1, 5);
}

After calling oExpire.AddCacheItem() the item is placed in the cache, and a callback delegate method was setup to be invoked when the item is removed from the cache. That method will get the latest data and repopulate the cache item, so users of the cache can always count on having most recent data (or possibly stale data if there are errors getting the updated data, which is better than no data).

Conclusion

The use of cache expirations and the technique of immediate repopulation while getting updated data provides many benefits. The site performs much better by using cached items rather than requiring web service or database calls. The use of an expiration callback delegate provides the ability to always have some data present by replacing the cached data, and tolerates problems requesting the new data. The use of expirations avoids the issue of race conditions if multiple requests arrived and each saw the data was out of date and attempted to get the new data, with expirations only that single method would attempt to get the new data.

Send comments or questions to robertb@aspalliance.com.



User Comments

Title: SQL 2008 support   
Name: Voodoo
Date: 8/12/2008 2:55:05 PM
Comment:
Is Query Notification Services supported in SQL 2008?
If not…
How are we going to move to the new SQL 2008 without a work around for Query Notification Services?
Title: Re: Henry   
Name: Robert Boedigheimer
Date: 7/31/2006 4:23:38 PM
Comment:
Hi, what I meant was that you definitely do not want to write the function that will be called in the code behind for the .aspx page because it will keep the page in memory too long. In the example above, the class cacheExpire is simply kept in a .cs file in the web site project itself (not a separate assembly or in the GAC). It is registered to handle the event by calling a method from that class (in the example oExpire.AddCacheItem( )) in the Page_Load of a page that wants the data to be added to the cache. Does that help? If you want more details you can email me at robertb@aspalliance.com to discuss.
Title: Where does class file reside?   
Name: Henry
Date: 7/31/2006 3:31:32 PM
Comment:
Hi, thanks for the information. But I'm confused where the Class file will reside and how it gets registered to handle the event. Do I compile into an Assembly and run it under Component Services or something? Or do I just drop it in the GAC? Sorry, just a confused on context.

...
Rather than waste memory, create a separate class file that will be used exclusively for the method.
...






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2014 ASPAlliance.com  |  Page Processed at 11/26/2014 3:41:33 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search