Caching
Author: Prakash Singh Mehra
Introduction: It is a way to store the frequently used data into the server memory which can be retrieved very quickly. And so provides both scalability and performance. For example if user is required to fetch the same data from database frequently then the resultant data can be stored into the server memory and later retrieved in very less time (better performance). And the same time the application can serve more page request in the same time (scalability).
Drawback: Suppose the server memory is filled with the data then the remaining part of the data is stored into the disk which slower the complete system performance. That's why self limiting caching techniques are best; where once the server memory gets filled the data has been selectively removed from the server memory to ensure that the application performance is not degraded.
Caching Types: Basically, caching is of two types:
- Output caching - The rendered html page is stored into the cache before sending it to the client. Now, if the same page is requested by some other client the already rendered htm page is retrieved from the server memory and sent to the client, which saves the time requires rendering and processing the complete page.
- Data Caching - The important pieces of information, that are time consuming and frequently requested, are stored into the cache. For example a data set retrieved from the database. It is very similar to application state but it is more server friendly as the data gets removed once the cache is filled.
There are two more models which are built on the above two types:
- Fragment caching - Instead of caching the complete page, some portion of the rendered html page is cached. E.g.: User Control used into the page is cached and so it doesn’t get loaded every time the page is rendered.
- Data Source Caching - It is caching built into the data source controls (eg. XmlDataSource, sqlDataSource etc). It is very similar to data caching but here the caching is not handled explicitly but the data source control manages it as per the settings made on the data controls.
Output Caching: The rendered html page is stored into the cache before sending it to the client. Now, if the same page is requested by some other client the already rendered htm page is retrieved from the server memory and sent to the client. Which saves the time requires rendering and processing the complete page. So saves the time taken to create the controls, start/run the page cycle again, and execute the code.
Declaration: To use output caching, declare the below line at the page:
<@ OutputCache Duration="20" VaryByParam="None">
It means that the page will be cached for the 20 seconds. If any client requests for the same page under this time, the rendered page is sent to it. After 20 seconds the updated/latest version of rendered page replaces the earlier version.
Note: The page is automatically removed when the application is recompiled.
Output Caching and Query String: Output caching also supports the query string values to cache the specific pages. On basis of query string parameter values, different pages are cached. For example, in the below declaration:
<@ OutputCache Duration="20" VaryByParam="ProductID">
Different version of page is cached based on different ProductId value. And later when the page is requested with specific query string value (ProductId value), the matching page is retrieved.
It is also possible to specify more than one query string parameter. In those cases the combined value of both the parameter work as a key for pages to be cached. There is also a special case, where "*" is passed as a parameter. In this case, pages are cached for all the separate combinations of the query string arguments.
<@ OutputCache Duration="20" VaryByParam="*">
Custom Caching Control: It provides a flexibility to user to cache the page based on their own built custom string. Let's take below example:
<@ OutputCache Duration="20" VaryByParam="None" VaryByCustom="Browser">
User has defined a custom string that will be used to generate the custom cache string. In this case, user wants to cache pages based on browser version. It requires the below method implementation under global.asax page (or should have the below structure):
Public override function GetVaryByCustomString(byval context as HttpContext, byval arg as string) as string
// Check for the requested type of caching.
If (arg == "browser") then
// Determine the current browser.
dim browserName as string = _
Context.Request.Browser.Browser + Context.Request.Browser.MajorVersion.ToString()
// Indicate that this string should be used to vary caching.
return browserName
Else if (arg=="SomeCustomString") then
//Define the custom cache value for the custom string
....................................................
Else MyBase.GetVaryByCustomString(context, arg)
End If
End Function
Caching using HttpCachePolicy Class: It contains methods to perform caching programmatically. Response.Cache property provides the instance of HttpCachePolicy
class. Let's see the below example:
Public sub CacheCurrentPage()
Response.Cache.SetCacheability(HttpCacheability.Public)
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60))
Response.Cache.SetValidUntilExpires(true)
End Sub
But, it's awkward to embedded caching through the code.
Fragment Caching: Sometimes it's required to cache only a portion of the page. In such scenarios, the portion is wrapped into a user control. Then the OutputCache directive is included in the user control file. In This way only user control part will be cached.
Cache Profiles: ASP.NET 2.0 introduces a new option that’s suitable if you need to apply the same caching settings to a group of pages. This feature, called cache profiles, allows you to define the caching settings in a web.config file. Use the <add> tag in the <outputCacheProfiles> section, as follows. You assign a name and a duration.
<configuration>
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="ProductItemCacheProfile" duration="60" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
...
</system.web>
</configuration>
You can now use this profile in a page through the CacheProfile attribute:
<%@ OutputCache CacheProfile="ProductItemCacheProfile" VaryByParam="None" %>
Cache Configuration: You can also configure various details about ASP.NET’s cache behavior through the web.config file. To configure these settings, you use the <cache> element inside the <caching> element described above.
<configuration>
<system.web>
<caching>
<cache disableMemoryCollection="true|false"
disableExpiration="true|false"
percentagePhysicalMemoryUsedLimit="number"
privateBytesLimit="number"
privateBytesPollTime="HH:MM:SS"/>
...
</caching>
</system.web>
...
</configuration>
- DisableMemoryCollection: stop ASP.NET from collecting items when memory is low
-
DisableExpiration: remove expired items.
-
PercentagePhysicalMemoryUsedLimit: set the maximum percentage of virtual memory that ASP.NET will use for the cache.
- PrivateBytesLimit: Determines the maximum number of bytes a specific application can use for its cache before ASP.NET begins aggressive scavenging(removal of older data from the cache).
- PrivateBytesPollTime:how often ASP.NET checks the private bytes used. The default value is 1 second.
Data Caching: The basic principle of data caching is that you add items that are expensive to create to a special built-in collection object (called Cache). This object works much like the Application object. However, a few key differences exist:
- The Cache object is thread-safe: This means you don’t need to explicitly lock or unlock the Cache collection before adding or removing an item. However, the objects in the Cache collection will still need to be thread-safe themselves.
- Items in the cache are removed automatically: ASP.NET will remove an item if it expires, if one of the objects or files it depends on is changed, or if the server becomes low on memory. This means you can freely use the cache without worrying about wasting valuable server memory.
- Items in the cache support dependencies: You can link a cached object to a file, a database table, or another type of resource. If this resource changes, your cached object is automatically deemed invalid and released.
But, as with application state, the cached object is stored in process, which means it doesn’t persist if the application domain is restarted and it can’t be shared between computers in a web farm.
Adding Items to the Cache: As with the Application and Session collections, you can add an item to the Cache collection just by assigning to a new key name:
Cache("Key") = “Value”
But the better approach is to use the Insert() method. It has four versions:
-
- Cache.Insert(key, value): Inserts an item into the cache under the specified key name, using the default priority and expiration.
- Cache.Insert(key, value, dependencies): Also includes a last parameter containing a CacheDependency object that links to other files or cached items and allows the cached item to be invalidated when these change.
- Cache.Insert(key, value, dependencies,absoluteExpiration, slidingExpiration): Also indicating sliding or absolute expiration policy(defined later on this section)
- Cache.Insert(key, value, dependencies,absoluteExpiration, slidingExpiration,onRemoveCallback): In addition, one can submit a delegate that points to a method you want invoked when the item is removed.
Sliding Expiration and absolute Expiration: Both can't be used at the same time. If absolute expiration is used, set the slidingExpiration parameter to TimeSpan.Zero. To set a sliding expiration policy, set the absoluteExpiration parameter to DateTime.Max.
With sliding expiration, ASP.NET waits for a set period of inactivity to dispose of a neglected cache item. Here’s an example that stores an item with a sliding expiration policy of ten minutes, with no dependencies, So, the data will be removed only if it is not used within a ten-minute period.
Cache.Insert("MyItem", obj, null, DateTime.MaxValue, TimeSpan.FromMinutes(10))
With absolute expiration, we set a specific date and time when the cached item will be removed. Here’s an example that stores an item for exactly 60 minutes:
Cache.Insert("MyItem", obj, null, DateTime.Now.AddMinutes(60), TimeSpan.Zero)
Cache Dependency: Basically cache dependency falls under two categories:
1. File and Cache Item Dependency: Cache item can be dependent on a particular file. On modification of this file, cache will be invalidated.
CacheDependency prodDependency = new CacheDependency(Server.MapPath("ProductList.xml"));
Cache.Insert("ProductInfo", prodInfo, prodDependency);
Cache can be dependent on other cached item as well. Create the array of cache keys and set the array as the dependency of other cache.
Cache["Key1"] = "Cache Item 1";
string[] dependencyKey = new string[1];
dependencyKey[0] = "Key1";
CacheDependency dependency = new CacheDependency(null, dependencyKey);
Cache.Insert("Key2", "Cache Item 2", dependency);
Also, a cache item can be dependent on multiple cache dependency objects. It’s called an aggregate cache dependency.
CacheDependency dep1 = new CacheDependency(Server.MapPath("ProductList1.xml"));
CacheDependency dep2 = new CacheDependency(Server.MapPath("ProductList2.xml"));
CacheDependency[] dependencies = new CacheDependency[]{dep1, dep2}
AggregateCacheDependency aggregateDep = new AggregateCacheDependency();
aggregateDep.Add(dependencies);
Cache.Insert("ProductInfo", prodInfo, aggregateDep);
2. Dependencies on a database query: cache items can be depenedent on sql queries as well. In SQL Server 2000 and earlier version it was tedious task but the same is simplified in SQL Server 2005. It requires following few steps, as mentioned below:
1. Enable cache notification - Make sure your database has the ENABLE_BROKER flag set. Assuming we’re using the Northwind database:
Use Northwind
ALTER DATABASE Northwind SET ENABLE_BROKER
2. Create the cache dependency - As per below code line.
string query ="SELECT EmployeeID, FirstName, LastName, City FROM dbo.Employees";
SqlCommand cmd = new SqlCommand(query, strConnectionString);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
// Fill the DataSet.
DataSet ds = new DataSet();
adapter.Fill(ds, "Employees");
// Create the dependency.
SqlCacheDependency empDependency = new SqlCacheDependency(cmd);
// Add a cache item that will be invalidated if one of its records changesor a new record is added.
Cache.Insert("Employees", ds, empDependency);
3. Call the static SqlDependency.Start() to initialize the listening service on the web server. This needs to be performed only once for each database connection. One place to call it is the Application_Start() method of the global.asax file.
4. Finally, call SqlDependency.Stop()
to detach the listener. Typically, this is called under Application_End() method.
Retrieving/Deleting the cache collection: There’s no method for clearing the entire data cache, but you can enumerate through the collection using the DictionaryEntry class. This gives you a chance to retrieve the key for each item and allows you to empty the class using code like this:
foreach item as DictionaryEntry in Cache
Cache.Remove(item.Key.ToString())
next
OR
foreach item as DictionaryEntry in Cache
itemList += item.Key.ToString()
Next
Special Point: When you retrieve an item from the cache, you must always check for a null reference. That’s because ASP.NET can remove your cached items at any time.
Cache Priorities: You can also set a priority when you add an item to the cache. The priority only has an effect if ASP.NET needs to perform cache scavenging, which is the process of removing cached items early because memory is becoming scarce. The data are deleted in following sequence:
Low, BelowNormal, Normal, AboveNormal, High
Low items are most likely to be deleted. There is one more priority,"NotRemovable"; these items will ordinarily not be deleted.