Caching in ColdFusion
ColdFusion allows you to cache data in several ways and is very simple to implement. Using caching features often produces a dramatic positive effect on overall site performance. Given these facts, it is strange the caching strategies are often an under-appreciated and under-used feature of ColdFusion.
You have two basic ways to cache in ColdFusion, one is in the CFML you write, and the other is in settings in the ColdFusion Administrator. In CFML, you can cache at the query level or at the page level. At the query level you can use either the cachedwithin or cachedafter attribute of the cfquery tag. At the page level you use the cfcache tag. In the ColdFusion Administrator there are three settings that deal with caching. All of these topics will be covered in this article.
Caching at the Query Level
It is very possible that the most time-consuming parts of your pages are the database queries, because of the resources associated with connecting to the database and retrieving and/or modifying the content in it. One way to avoid constantly querying the database is to use cached queries, which are queries saved in the ColdFusion application server memory.
Good candidates for cached queries are queries that:
- Do not change very often, such as a product catalog, or a list of countries.
- Your app uses on multiple pages.
- Multiple users use.
One method for caching queries is to use the cachedwithin or cachedafter attributes with the cfquery tag. This technique lets you cache a particular query for a specific period of time. The following table lists the pros and cons of query-based caching:
| Advantages | Disadvantages |
|---|---|
| Can dramatically enhance performance | Uses unknown and uncontrollable amounts of memory, although you can limit the number of cached queries, but not the amount of memory they use |
| Simple to implement | You cannot explicitly manage it |
| Well-suited for dynamic queries |
Using the cachedwithin Attribute
Use the cachedwithin attribute to specify the time period for which to cache a query. The following query uses the same data for one full day before it refreshes it from the database:
<cfquery name="qCountry" datasource="devnet"
cachedwithin="#CreateTimeSpan(1,0,0,0)#">
SELECT Country_ID, Country_Name
FROM Country
ORDER BY Country_Name
</cfquery>
Note: The arguments of the CreateTimeSpan( ) function are (days, hours, minutes, seconds).
When you cache a query, you can refer to it by using the same query syntax:
- The values of the name and datasource attributes must be the same, and are case sensitive.
- The resulting SQL statements must be the same, including spaces, returns, tabs, and so on. In addition, the case of all SQL statements must also be the same.
If any of this information differs, ColdFusion does not use the cached query.
The first time a user browses a page, the following appears in the debugging information:
SQL Queries
qCountry (Datasource=devnet, Time=110ms, Records=9) in C:\CFusionMX\wwwroot\devnet_cache_development\cachedwithin.cfm @ 05:10:24.024 SELECT Country_ID, Country_Name FROM Country ORDER BY Country_Name ASC
When users browse the page again, the debugging reports the following:
SQL Queries
qCountry (Datasource=devnet, Time=0ms, Records=9, Cached Query) in C:\CFusionMX\wwwroot\devnet_cache_development\cachedwithin.cfm @ 05:12:48.048 SELECT Country_ID, Country_Name FROM Country ORDER BY Country_Name ASC
Note that the query time changes from 110 milliseconds to 0 milliseconds, and logs that the query is cached. ColdFusion caches this particular query for one day (based on the CreateTimeSpan() function). After the day has expired, the next user that browses the page would have to wait for the query to be performed again, and it would be cached again.
Using the cachedafter Attribute
This section of the article is taken from the Advanced ColdFusion Development Instructor-Led Training course and was also featured in Sue Hove's article on Building Advanced Queries in ColdFusion MX. Use the cachedafter attribute when you want to refresh the query at a certain time each day. If large volumes of database writes regularly occur between midnight and 2:00 AM, you could refresh the query at 2:00 AM every day, and use that cached query until 12:00 AM the next day.
The following functions are often used with the cachedafter attribute:
| Function | Description |
|---|---|
| CreateDateTime() | Creates a date/time object using the syntax CreateDateTime(year, month, day, hour, minute, second) |
| Year() | From a date/time object, gets the year value |
| Month() | From a date/time object, gets the month value |
| Day() | From a date/time object, gets the day value |
| Now() | Gets the current date and time of the computer running the ColdFusion server |
Here is the code for a cfquery tag that caches the query and updates it at 2:00 AM every day:
<cfquery name="qGetCountry" datasource="devnet"
cachedafter="#CreateDateTime(Year(Now()),
Month(Now()),
Day(Now()),
2,0,0)#">
Note: The cachedafter attribute in this code breaks the day into two parts. From midnight to 2:00 AM, no caching is done. The first time a user browses a page with the query after 2:00 AM, ColdFusion caches the query again and uses it until midnight that day.
Flushing Cached Queries
You can immediately refresh the queries that use the cachedwithin or cachedafter attribute by inserting all zero values in the CreateTimeSpan( ) function, as shown here:
<cfquery name="qGetCountry" datasource="devnet"
cachedwithin="#CreateTimeSpan(0,0,0,0)#">
SELECT CountryID, CountryName
FROM Country
ORDER BY CountryName
</cfquery>
Limiting the Number of Cached Queries
In ColdFusion Administrator, you can control the total number of queries cached. In the ColdFusion Administrator, select Server SettingsàCaching, and you will see the entry for the setting. It appears as shown here:
Note that this does not control the total amount of memory used by the cached queries, just the total number of cached queries. If ColdFusion server reaches the maximum number of queries in this setting, ColdFusion removes an old query to make room for the new query.
Caching Output
Another caching strategy in ColdFusion is preventing ColdFusion from doing the same work over and over again. ColdFusion provides a mechanism to improve application performance by caching pages instead of dynamically rendering them each time. Once ColdFusion has built the correct HTML for a CFML page, you can save that HTML page and return that to the browser, rather than processing the CFML file again. Use the cfcache tag to implement this behavior.
Using the cfcache Tag
Use the cfcache tag when you wish to cache the entire page. When you use this tag to perform server-side caching, ColdFusion saves the rendered HTML and then uses it on all subsequent requests for the same page. For example, consider the following CFML page:
<cfcache action="cache" timespan="#CreateTimeSpan(0,1,0,0)#">
<cfquery name="qBeanInfo" datasource="devnet">
SELECT Bean_ID, Bean_Name
FROM Beans
ORDER BY Bean_Name
</cfquery>
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<table>
<tr>
<th>Bean_ID</th>
<th>Bean_Name</th>
</tr>
<cfoutput query="qBeanInfo" maxrows="2">
<tr>
<td>#qBeanInfo.Bean_ID#</td>
<td>#qBeanInfo.Bean_Name#</td>
</tr>
</cfoutput>
</table>
</body>
</html>
This creates a cached file that looks like this:
<!---http://localhost:8500/devnet_cache_development/cfcache1_example.cfm--->
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<table>
<tr>
<th>Bean_ID</th>
<th>Bean_Name</th>
</tr>
<tr>
<td>2</td>
<td>Colombian Supremo</td>
</tr>
<tr>
<td>5</td>
<td>Costa Rican</td>
</tr>
</table>
</body>
</html>
As you can see, the cached page contains a comment that it is cached, and the HTML that ColdFusion generated. ColdFusion can now return the finished HTML page on all subsequent requests, rather than dynamically building it again and again.
Understanding cfcache Details
The cfcache tag has a number of attributes. The two used in the example are action and timespan. The action attribute can have the following values:
- cache: Server-side and client-side caching (this is the default if no action attribute used)
- flush: Refresh cached page(s)
- clientcache: Browser-side caching only (to cache a personalized page, use this option)
- servercache: server-side caching only (not recommended)
- optimal: Same as "cache"
The cfcache tag is smart enough to understand that if different URL parameters are passed to the page, that ColdFusion should rebuild the page dynamically for each unique request. For example, on a master page, you have the
following code:
<a href="cfcache_test.cfm?x=1">1</a> <a href="cfcache_test.cfm?x=2">2</a> <a href="cfcache_test.cfm?x=3">3</a>
If on the cfcache_test.cfm target page you had the following cfcache tag:
<cfcache action="cache">
ColdFusion would cache three different pages, one for each separate URL parameter.
Caching on the Server-Side
When ColdFusion processes a cfcache tag with an action attribute value that specifies using server-side caching, the following occurs:
- ColdFusion renders the page into HTML as would normally occur.
- ColdFusion returns the page to the web server to be sent back to the requester, as would normally occur.
- ColdFusion saves the rendered file as a static HTML file, as directed by the value of the action attribute. The default location is C:\CFusionMX\cache. If you use the directory attribute, it overrides the default location and will save the file in the location specified in the directory attribute. Note that the value must be an absolute path.
-
ColdFusion creates the name of the file using the following format cfcache_HASH.tmp, where HASH uses the ColdFusion Hash() function to convert the template's URL into a 32-byte, hexadecimal string. A specific example would be:
- cfcache_05556737CB141B205B836A3B98AAC1F0.tmp
- The ColdFusion application server serves this fully-rendered, saved HTML file, instead of rendering the page each time a browser requests the page. The file is retrieved by again using the Hash() function on the URL and selecting the correct cached file.
Caching on the Client-Side
Even more efficient than having the ColdFusion server return an already rendered page is to have the client use the page it previously requested. This is even less work for the ColdFusion server. The cfcache tag can also implement caching on the
client-side. To turn client caching on, set the value of the action attribute of the cfcache tag to either cache (which is both client and server side caching), optimal (which is the same as cache),
or clientcache. Any of these values enables client-side caching.
Clent-side caching is implemented using standard HTTP caching mechanism that web servers and browsers support. Most browsers keep a cache of recently accessed pages on their host machine, and the HTTP protocol provides a way to signal when it is ok for the browser to use cached data instead of requesting it from the server again. Web servers usually do this automatically for static HTML, JPEG, etc. files by looking at the last modified dates of the files. The cfcache tag provides a way to tag this for dynamic CFML pages. When you have client-side caching enabled, ColdFusion adds the "Last-Modified" HTTP response header. The browser stamps its cache of this URL with this date. Then, the next time it requests the URL, it adds the "If-Modified-Since" HTTP request header, with that date.
The cfcache tag checks the following three conditions:
- The request contains the "If-Modified-Since" response header.
- The cached template has not timed out according to the timespan attribute.
- The source .cfm template has not changed.
If all of these conditions are true, ColdFusion replies with a simple "304" HTTP response, which tells the browser to use its cached data.
Specifying How Long to Use the Cached File
You can specify how long to cache a file by including the timespan attribute. Use the ColdFusion function CreateTimeSpan() to specify how long to use the cached file. The arguments of the CreateTimeSpan() function are (days, hours, minutes, seconds).
Flushing Cached Pages
If you wish to manually flush cached pages, rather than let them timeout, you can do so using the action="flush" attribute/value pair. Use the following code,
<cfcache action="flush">
This deletes all the rendered and cached files in the form cfcache_HASH.tmp. If you use no directory attribute, this action removes all the files from C:\CFusionMX\cache. If you specify a directory, it removes all the rendered and cached files from that particular directory.
If you wish to flush a particular file, use the action="flush" attribute/value pair with the addition of the expireurl attribute. So to delete the cached and rendered file associated with the file
cfcache1_example.cfm, you would use the following:
<cfcache action="flush" expireurl="http://server/path_to_file/cfcache1_example.cfm">
The line of code uses the full URL to the location of the file that was cached. If you used a directory attribute when caching the page, you will also need to provide the same attribute when flushing it.
Earlier I showed how the cfcache tag generates multiple files for the same page if different URL parameters were passed to the ColdFusion page. To flush all of the files associated with one page, with different URL parameters, you
can use the following syntax:
<cfcache action="flush" expireurl="http://server/path_to_file/cfcache_test.cfm?x=*">
This removes all the files that ColdFusion created by calling the file cfcache_test.cfm and the URL parameter x equal to any value.
Caching ColdFusion Templates
The first time a ColdFusion template is browsed, the CFML template is compiled into a Java class file and saved to disk. Once this complied file is created it is not recompiled unless changes are made to the CFML template. This complied class file is then used to create the HTML file that is sent back to the user. Rather than having to read the Java class file every time a page is requested, ColdFusion will automatically cache some, or all, of these files.
There are two settings in ColdFusion Administrator that affect how this type of caching works. They are:
- The maximum number of Java class files.
- Whether ColdFusion should check to see if the actual source CFML files from which the Java class files were built have changed.
Setting the Maximum Number of Templates Allowed to be Cached
The first setting to check to ensure proper caching is the Template cache size (number of templates). In the ColdFusion Administrator, select Server SettingsàCaching, and you will see the entry for the setting. It appears as shown here:
This setting specifies the maximum number of Java classes (even though it says templates) ColdFusion will cache. Given sufficient memory, set this value to at least the total number of ColdFusion templates on your server. Even if the number is too high, the files are not cached until a user actually requests them.
Enabling Trusted Cache
By default, before ColdFusion uses a Java class, it will take the time to check if the original CFML template has changed since it created the cached file. If you have a well-established site that is not changing, there is no reason to make ColdFusion take the extra time to do this. In the ColdFusion Administrator, select Server SettingsàCaching, you will see the checkbox for Trusted Cache. It appears as shown here:
When you check this box, ColdFusion will not check to see if the file that was used to generate the cached file has been updated. Obviously, if you make updates to your site and trusted cache is on, you will not see the new files. If you want the updated files to be placed into cache you must:
- Uncheck the Trusted Cache setting in ColdFusion Administrator and click the Submit Changes button.
- Browse the templates that were changed.
You can then check the Trusted Cache button again. If you have changed many, many templates and do not want to have to browse every template changed, you can simply stop and start the ColdFusion Application Server. Then ColdFusion will cache each template's class file when a user browses it, but not check it again if Trusted Cache is turned on.
Summary
Caching is straightforward to implement, and usually yields significant performance gains. In your code, you can use either query caching to help performance when accessing the database, or use the cfcache tag to cache rendered CFML pages and send those instead of having to process pages over and over again. In ColdFusion Administrator, you can change settings that effect overall caching of compiled ColdFusion templates and cached queries.