Caching parts of ColdFusion pages

In some cases, your ColdFusion page contain a combination of dynamic information that ColdFusion must generate each time it displays the page, and information that it generates dynamically, but that change less frequently. In this case, you cannot use the cfcache tag to cache the entire page. Instead, use the cfsavecontent tag to cache the infrequently changed content.

The cfsavecontent tag saves the results of processing the tag body in a variable. For example, if the body of the cfsavecontent tag contains a cfexecute tag that runs an executable program that displays data, the variable saves the output.

Use the cfsavecontent tag to cache infrequently changing output in a shared scope variable. If the information is used throughout the application, save the output in the Application scope. If the information is client-specific, use the Session scope. Because of the overhead of locking shared scope variables, use this technique only if the processing overhead of generating the output is substantial.

Before you use this technique, also consider whether other techniques are more appropriate. For example, query caching eliminates the need to repeat a common query. However, if the effort of processing the data or formatting the output is substantial, using the cfsavecontent tag saves processing time.

Using this technique, if the variable exists, the page uses the cached output. If the variable does not exist, the page gets the data, generates the output, and saves the results to the shared scope variable.

The following example shows this technique. It has two parts. The first part welcomes the user and prints a random lucky number. This part runs and produces a different number each time a user opens the page. The second part performs a database query to get information that changes infrequently; in this example, a listing of the current special sale items. It uses the cfsavecontent tag to get the data only when needed.

If you use this technique frequently, consider incorporating it in a custom CFML tag.
<!--- Greet the user. ---> 
<cfoutput> 
    Welcome to our home page.<br> 
    The time is #TimeFormat(Now())#.<br> 
    Your lucky number is: #RandRange(1,1000)#<br> 
    <hr><br> 
</cfoutput> 
 
<!--- Set a flag to indicate whether the Application scope variable exists.---> 
<cflock scope="application" timeout="20" type="readonly"> 
    <cfset IsCached = IsDefined("Application.ProductCache")> 
</cflock> 
 
<!--- If the flag is false, query the DB, and save an image of 
    the results output to a variable. ---> 
<cfif not IsCached> 
    <cfsavecontent variable="ProductCache"> 
    <!--- Perform database query. ---> 
        <cfquery dataSource="ProductInfo" name="specialQuery"> 
            SELECT ItemName, Item_link, Description, BasePrice 
            FROM SaleProducts 
        </cfquery> 
<!--- Calculate sale price and display the results. ---> 
        <h2>Check out the following specials</h2> 
        <table> 
        <cfoutput query="specialQuery"> 
            <cfset salePrice= BasePrice * .8> 
            <tr> 
                <td>#ItemNAme#</td> 
                <td>#Item_Link#</td> 
                <td>#Description#</td> 
                <td>#salePrice#</td> 
            </tr> 
        </cfoutput> 
        </table> 
    </cfsavecontent> 
 
<!--- Save the results in the Application scope. ---> 
    <cflock scope="Application" type="Exclusive" timeout=30> 
        <cfset Application.productCache = ProductCache> 
    </cflock> 
</cfif> 
 
<!--- Use the Application scope variable to display the sale items. ---> 
<cflock scope="application" timeout="20" type="readonly"> 
    <cfoutput>#Application.ProductCache#</cfoutput> 
</cflock>

Reviewing the code

The following table describes the code and its function:

Code

Description

<cfoutput> 
    Welcome to our home page.<br> 
    The time is #TimeFormat(Now())#.<br> 
    Your lucky number is: #RandRange(1,1000)#<br> 
    <hr><br> 
</cfoutput>

Displays the part of the page that must change each time.

<cflock scope="application" timeout="20" type="readonly"> 
    <cfset IsCached = IsDefined("Application.ProductCache")> 
</cflock>

Inside a read-only lock, tests to see if the part of the page that changes infrequently is already cached in the Application scope, and sets a Boolean flag variable with the result.

<cfif not IsCached> 
    <cfsavecontent variable="ProductCache">

If the flag is False, uses a cfsavecontent tag to save output in a Variables scope variable. Using the Variables scope eliminates the need to do a query (which can take a long time) in an Application scope lock.

<cfquery dataSource="ProductInfo" name="specialQuery"> 
SELECT ItemName, Item_link, Description, BasePrice 
FROM SaleProducts 
</cfquery>

Queries the database to get the necessary information.

<h2>Check out the following specials</h2> 
<table> 
    <cfoutput query="specialQuery"> 
    <cfset salePrice= BasePrice * .8> 
    <tr> 
        <td>#ItemNAme#</td> 
        <td>#Item_Link#</td> 
        <td>#Description#</td> 
        <td>#salePrice#</td> 
    </tr> 
    </cfoutput> 
</table>

Displays the sale items in a table. Inside a cfoutput tag, calculates each item’s sale price and displays the item information in a table row.

Because this code is inside a cfsavecontent tag, ColdFusion does not display the results of the cfoutput tag. Instead, it saves the formatted output as HTML and text in the ProductCache variable.

</cfsavecontent>

Ends the cfsavecontent tag block.

<cflock scope="Application" type="Exclusive" timeout=30> 
    <cfset Application.productCache = ProductCache> 
</cflock>

Inside an Exclusive cflock tag, saves the contents of the local variable ProductCache in the Application scope variable Application.productCache.

</cfif>

Ends the code that executes only if the Application.productCache variable does not exist.

<cflock scope="application" timeout="20" type="readonly">     <cfoutput>#Application.ProductCache#</cfoutput> 
</cflock>

Inside a cflock tag, displays the contents of the Application.productCache variable.