Nov
22

Today's caching example moves us away from the template cache and into the object cache. I defined these two types of caches on day three of this series but let's revisit the definitions. The template cache in ColdFusion 9 refers to a specific area of cache where page fragments and full page cache items are stored. The object cache refers to anything cached except pages including strings, arrays, ColdFusion components, queries, XML, or the like.

Let's look at a simple example that illustrates the use of three new functions in ColdFusion 9: cachePut(), cacheGet(), and cacheGetMetadata().

<!--- Attempt to retrieve the getOrders query from cache. Also retrieve the cache
metadata at the same time. --->

<cfset cachedData = cacheGet("wt-6-cache")>

<!--- If the data is not cached, create it and do a cache put. --->
<cfif isNull(cachedData)>
    Cache doesn't exist, so create it.<br />
    <cfset sleep(1000)>
    <cfset cachedData = "This date/time IS cached: #Now()#<br />">

    <cfoutput>#cachedData#</cfoutput>
    <cfset cachePut("wt-6-cache", cachedData, CreateTimeSpan(0,0,0,10))>
</cfif>

<cfoutput>This date/time is not cached: #Now()#</cfoutput>

<h4>Cached data</h4>
<cfdump var="#cachedData#">

<h4>Cached metadata</h4>
<cfdump var="#cacheGetMetadata('wt-6-cache')#">

The concept illustrated here creates a result similar to the example from day three. The difference between the two is the cache type used. Instead of caching a page fragment with a displayed date/time, we simply cache the date/time string in object cache. The first bit of code in the code block attempts to retrieve an item from object cache with the key "wt-6-cache." If there isn't an item in object cache matching the key, the cacheGet() function returns null. The second bit of code uses the new isNull() function (bonus!) to check the result of the cacheGet() function. If the item isn't cached we simulate a slow page with the sleep() function, output a simple date/time string, and then call the cachePut() function. This function has two required parameters, cache ID/key and cache value, followed by two optional parameters cache timespan and idletime. The signature of the function looks like this: cachePut(id, value[, timeSpan[, idleTime]]).

Next, we use the <cfoutput> tag to display a date/time string that isn't cached. Then, we use the <cfdump> tag to display the contents of the cache. And finally, we use the new cacheGetMetadata() function inside a <cfdump> tag to display cache metadata for the cached value with key "wt-6-cache."

The first time you execute the page the output looks like this:

The cacheGet() function returns null since the item has never been cached. This also causes the isNull() function to evaluate to true which in turns causes the string "Cache doesn't exist, so create it." to display. Two date/times are displayed, the first one coming from the <cfoutput> just before the cachePut() function and the second which will display every time the page is loaded. At the bottom of the page is the metadata that comes from the cacheGetMetadata() function. Here's a list describing each piece of metadata.

Caching terminology:

  • cache hitcount - number of times a cache hit has occurred for any item in the cache
  • cache miscount - number of times a cache miss has occurred for any item not in cache or that was stale
  • created time - the date/time the cached item was created
  • hitcount - number of times the cached object has been requested and was used
  • idletime - the cache idletime as set in the cachePut() function
  • last hit - the date/time the cached item was last requested/used
  • last updated - the date/time the cached item was last modified
  • size - number of bytes the cached item occupies when serialized
  • timespan - the cache timespan as set in the cachePut() function

If you run the template a second time before the timespan of 10 seconds expires you'll see output that looks like this.

Here we don't see the string "Cache doesn't exist, so create it.," and only two date/times appear. The first one isn't cached, and the second comes directly from the cache showing different times. Notice how the cache metadata has changed too. The cache_hitcount value increased by one showing us that all cache hits increment this data point. The regular hitcount value also increased by one showing us this value increases each time a cached item is retrieved. Note, that when the cache timespan (or idletime if defined) is reached the cached item will be removed from cache and the hitcount value reset to zero. Finally, we also see the lasthit value now has a date/time and the date/time matches the one at the top of the page.

If you allow this specific cached item to expire a few times and you hit the page again and again you'll eventually create metadata that looks like this.

Here you can see the cache_hitcount value continue to rise as we retrieve the item with cacheGet(). The cache_miscount value has also increased to show how may times the cache has expired and been requested again. If you haven't realized by now how much more metadata you have when using object cache versus template cache you should. You can use this additional metadata to analyze cache items and determine their validity as well as ensure your cache timespan and idletime are values that optimize cache hits and minimize cache misses.

The object cache is also much more flexible than template cache. You can store anything you want and you have control over the cache key name giving you the ability to be more creative and descriptive. However, remember that template and object cache on ColdFusion 9 occupy memory space (Java Virtual Machine or JVM) by default. If you have multiple applications or users on your server you want to be careful when creating names for your cache keys as you could create a collision with an existing cache item. If this were to happen you could read incorrect and unsuspected values from cache or worse yet, replace an item in cache with unrelated data.

Click here to download the code mentioned in this post.

Aaron West's Gravatar
About this post:

This entry was posted by Aaron West on November 22, 2009 at 8:45 PM. It was filed in the following categories: ColdFusion. It has been viewed 11955 times and has 7 comments.

13 related blog entries

7 Responses to 14 Days of ColdFusion 9 Caching: Day 6 - Using cachePut, cacheGet, and cacheGetMetadata

  1. Are you sure you can get cache key collisions?
    I thought that ColdFusion was creating a different EHCache CacheManager for each application, based on your defaultCache settings (defined in ehcache.xml).
    But I'm not sure...

  2. I just want to be on this for the response to Benoit's question. My gut would say that it was app-specific; however, CFLock (named-locks) are apparently server-wide, not app-wide, so it would not shock me if the cache was sever-wide.

  3. @Benoit - You are correct about how ColdFusion manages the caches by default. The ColdFusion 9 server uses the settings in ehcache.xml to create two default caches. One for template cache and one for object cache. These are called cache regions and you can create additional regions by editing the ehcache.xml file.

    So, by default you have two caches and when they are created at startup time they have the names appnameOBJECT and appnameTEMPLATE. So yes, two different applications can use the same key name for a specific cached item and they won't collide with one another. But, they are effectively stored in the same cache region. I'm simply recommending folks be careful with their names and be mindful of what keys are used where. Even if you can't collide across applications, you can within an application and that's still just as bad.

  4. I know that this is an older post, but when I was doing my "Best of" CF9 entry, I started playing around with the cache for the first time. One thing that I found useful was factoring out the key used in the cache into it's own method call. For example, if I had a given object with a given ID to cache, I might have:

    getCacheKey( id )

    ... and then use result of that to do the cachePut() / cacheGet() calls.

    This kept the intent and the implementation seperate and made retrieving stuff from the cache in different places easier (at least for me).

  5. Aaron,

    Really late to the party here, but so far these tutorials are simply fantastic.

    I have a question about a real-world usage idea for cachePut and cacheGet. I am really really weak in caching, mainly from a "how would I use this?" perspective.

    I have an application that generates a large form of three-tiered checkboxes for a report. The tiers are categories, groups, and sub-groups. It's fairly slow loading each time, and something I wanted to cache.

    I would like to cache this so it doesn't have to be rebuilt each time, but there is a complicating factor of that, through an admin tool, the clients can add, update, reorganize, or delete items from these tiers.

    So, if I were to generate the form HTML for these checkboxes in a CFSAVECONTENT block, and place that variable in the cache with cachePut(), I could call it from the form page with cacheGet()? And, upon a change in the structure of these tiers, I could add a part to the action code for the admin piece to remove that object from the cache, and the form would rebuild it once it sees the cache object is not there?

    Is that an okay solution, or where should I be looking to handle this?

  6. I would also add that cache may persist through application restart. Call applicationStop() doesn't clear the cache.

  7. ( I don't know if it is a cache issue, but when I post a comment on your blog, I can't see my entry directly. I had to refresh ;) )