First off, Happy Thanksgiving to my fellow Americans!! I hope each of you have an awesome day; I know I will. Today's caching example will be a little shorter than my previous examples but hey, it's a holiday.

ColdFusion 9 doesn't have built in session-based caching when it comes to the template or object cache. But, we've had shared scopes such as Server, Application, and Session for as long as I can remember. Using shared scopes in CF applications are pretty much a de facto standard as they're central to building applications that maintain state and store data across minutes, hours, days, or more. Now, consider how Ehcache in ColdFusion 9 works. By default there's one cache region defined in the ehcache.xml file. Within this region of cache exist the template and object cache. Each Application on your ColdFusion server will have a shared template and object cache. This is nice as it allows you to build cache keys inside of one application that won't collide with cache keys in another. But sometimes you want even more granular storage of cached data than this. You might want to associate a cached item with a specific user and have the code work with any number of users visiting your site. A shopping cart is a common example of this type of behavior.

In order for a session-based shopping cart to work you'd need to ensure sessions were enabled in your application. To do this, create a simple Application.cfc file that looks like the following.

<cfcomponent output="false">
    <cfset = "SessionCaching">
    <cfset this.sessionmanagement = true>
    <cfset this.sessionTimeout = CreateTimeSpan(0,0,0,30)>
    <cffunction name="onSessionStart" returntype="void">
        <cfset session.uniqueID = CreateUUID()>

The code here is run-of-the-mill app.cfc with an onSessionStart function that, as you might guess, fires each time a new session is started. Inside the onSessionStart function I've added code that creates a unique identifier (UUID) for every user. Sure each session already has a urltoken that represents the same sort of identifier, but managing the ID with code like this is more flexible. For instance, you could update the onSessionStart function to retrieve userID's from a database and use those as unique identifiers instead of UUIDs.

Now considering the following code snippet that could be part of a shopping cart system.

<cfset shoppingCartCache = "ShoppingCart|" & session.uniqueID>

<cfset userShoppingCart = cacheGet(shoppingCartCache)>

<!--- If the shopping cart is not already cached, put it in the cache. --->
<cfif isNull(userShoppingCart)>
    <cfset userShoppingCart = [{item = "Pillow-#Right(CreateUUID(), 4)#", price = "29.99"}, {item = "Blanket-#Right(CreateUUID(), 4)#", price = "49.99"}]>
    <cfset cachePut(shoppingCartCache, userShoppingCart)>

Session specific cache value:
<cfdump var="#userShoppingCart#">

You probably wouldn't see code like this placed in a single template in a normal application. You'd likely have an add orders template or service objects responsible for managing orders. Anyhow, the first line of code creates a variable we'll use as the key for our session specific shopping cart cache. This is made up of an identifier specific to the type of thing we are storing in cache (shopping cart) followed by the session UUID created in the onSessionStart function of Application.cfc.

Next, we retrieve the shopping cart from object cache. If the shopping cart with our session specific name doesn't exist the cacheGet() function will return null. This allows us to use the new isNull() function in ColdFusion 9 to determine what to do next. Normally, you'd add items to a shopping cart when the user requested you to do so. Here, we simulate a shopping cart by adding two items whenever the cart is null. The pillow item and the blanket are both augmented with 4 characters from a UUID so we can be sure our example works when we test it. Next, I use the cachePut() function to store the users shopping cart contents in object cache with the session specific key. Last, I use the <cfdump> tag to display the contents of the user shopping cart.

The first time this page runs the value of the shopping cart is set in the <cfset> statement. All subsequent refreshes - as long as the session hasn't expired - retrieve the cart contents from cache using the cacheGet() function. The code inside the <cfif> never executes in this case.

Here's the output from running this template in Safari.

And here's the output seen in Firefox a few seconds later. Since I'm running two different browsers, two different sessions are fired up with two separate unique session identifiers and ultimately two different shopping carts stored in cache.

When it comes to shopping carts and other session rich applications, there's nothing really new here. We're just utilizing Ehcache instead of shared scopes by themselves. This is pretty significant though. Sessions and other shared scopes certainly have a defined timespan based on settings in Application.cfc or, if you're kicking it old school, Application.cfm. But, the session scope and application scope "stay alive" as long as your application is getting hit or the user is active in the application. That's all well and good but it isn't as flexible as caching. With caching you can make use of the timespan attribute as well as the idletime attribute to manage the life of the cache. Not to mention you can leverage cache metadata to examine cache hits and misses and determine what in your application is working well and what isn't. You always want to minimize cache misses and maximize cache hits, but unless you wrote your own code to do this with sessions and applications it just wasn't possible out of the box. That to me is another win for caching in ColdFusion 9.

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 26, 2009 at 8:00 AM. It was filed in the following categories: ColdFusion. It has been viewed 11919 times and has 0 comments.

13 related blog entries

0 Responses to 14 Days of ColdFusion 9 Caching: Day 10 - Session-Specific Caching