One for the Money ...
How to Really Cache In with Just a Few Lines of Code
By James Walters
Virtually every month you can find an article somewhere that describes caching strategies and how to go about improving performance. Almost every developer knows the word or has implemented some sort of caching at one point. After all, there are several types of caching, including data caching, file caching, and even HTML output caching.
This month we re going to take a look at some specific problems and describe where and when caching is appropriate using the ASP.NET Caching API.
What s Not to Love?
Let s dig right in with caching data files; in this case, XML files. The last few years, XML has really taken off as the preferred way to store or transfer bits of hieratical data in a platform-independent manner. What s not to love? We have XML data files, XSD definitions, XSL, RSS Feeds it s even used as the basis for SOAP and Web services. And while most of us know XML is a little bulkier, we eventually embrace it because it s so flexible and easy to understand.
Now that everyone is using XML, almost every experienced developer has at some point deployed an application, only to have someone else screaming three hours later that the new Web site is crawling. Usually the first thing we say is, Hmmm it worked great on my box.
Unless you have a load testing environment, these files are easy to overlook because everything does work great when we re in that single-user mode of development. But we all know that hard-drives are not nearly as fast as application memory, and you ll bottleneck at the drive without caching on any Web site that s under even a light constant load. So how do we correct this?
Whether it s with an extravagant object-oriented framework or simply reading the file from various areas in your code, it doesn t matter how you implement your usage of XML. The simple function shown in Figure 1 will help you get the most out of your code by checking to see if your XML document is cached before fetching it from the file system. As you can see, the Cache.Insert method is using something called a Cache Dependency. The dependency in this case is actually the file itself; if any changes are made to the file, the .NET Framework is smart enough to invalidate your cached data so that the next read will get the latest file and cache it.
Figure 1: This simple function will help you get the most out of your code by checking to see if your XML document is cached before fetching it from the file system.
As a side note, when dealing with reference type objects (such as XmlDocuments, DataTables, or anything in this article), it s important to understand that we don t expect the calling code to modify the cached object, unless the intent is to modify it for all usages. If, for some reason, your code is modifying the cached data on a per-user basis, you ll need to return a copy of the cached data to the calling code or you ll most likely have incorrect data or even memory access issues.
About to Expire
Now that we ve seen how to cache a file indefinitely (until the file is changed), let s imagine that your Web site is driven from hundreds, or even thousands, of data files. This is a scenario where having thousands of file watchers really isn t the best approach, and if your application doesn t require the latest version of every file, then cache expirations would be more appropriate.
There are two types of these expirations: absolute expiration and sliding expiration. The absolute expiration is simply a future date and time when the cached object will be automatically invalidated; the sliding expiration is like a countdown that resets every time the cached object is accessed, or if the object isn t accessed within x amount of time, it would be invalidated.
Which is better? That depends. For data files that will be used by the majority of Web site users, an absolute expiration makes more sense. But if the data will be used by a small percentage of users, the sliding expiration with a fairly short time span will probably benefit you more, because it allows data to be cached on more of an as needed basis.
As we move in to perhaps the most common implementation of caching, which is to cache database lookup tables, an example of the absolute expiration is shown in Figure 2. Because we developers seem to have 1,001 ways of retrieving data, I won t try to suggest the best way of accessing your database, nor will I try to dictate the best way to maintain tier separation.
Figure 2: This code is very similar to our strategy for caching XML files. If the data table is not cached we ll retrieve the latest data and cache it for four hours.
Looking at Figure 2, the code is very similar to our strategy for caching XML files. If the data table is not cached, we ll retrieve the latest data and cache it for four hours. I ve seen very elegant designs that utilize the Web.config file to specify individual cache timeouts for each lookup table, and simple implementations like this where everything is hard-coded to be cached for a fixed time period; your implementation could be different, as well.
Additionally, much like the file-based CacheDependency we used in Figure 1, both .NET 1.1 and 2.0 include the SqlCacheDependency class, which essentially will detect changes in your database and invalidate your cached data (forcing a refresh on the next call).
When caching data from the database, I generally prefer to use an absolute expiration to maximize the overall performance gain. However, if your lookup tables are updated throughout the day, or occasional changes to these tables must be immediately reflected in the application, I recommend using the SqlCacheDependency class. And, of course, if the table in question is frequently changing, perhaps it s best to stick with live data.
There are numerous ways to cache data. These are just a few that I have found to be extremely helpful and easy to implement. If you re interested in even more performance gains and you haven t heard about HTML output caching, there are several flavors involved that deserve a separate article; more information is located at http://msdn.microsoft.com.
James Walters is a Senior Software Engineer with Vertigo Software, Inc., a Microsoft Gold Certified Partner for eCommerce Solutions. James is MCSD.NET certified; his primary role includes designing and building scalable, high-performance applications using the .NET Framework. James is also technical editor for asp.netPRO and asp.netNOW.