Recording, Slides and Demo Code now Available from my ColdFusion/Ehcache Webinar

The Webex recording, Powerpoint slides and example code I used in last week's Boosting Performance and Scale for ColdFusion Applications with Ehcache webinar are now available. You can watch the recording here (note that registration is required):

Boosting Performance and Scale for ColdFusion Applications with Ehcach

Additionally, you can view the presentation slides:

Alternately, you can download the slides.

You can also download the sample code.

Please note that the examples created for blogCFC should only be considered a POC to demonstrate concepts and not production ready code.

Issue with Ehcache and ColdFusion Query Objects

I submitted a bug for this but the system seems to have swallowed it up without giving me the bug number (probably because I submitted as cf 9.0.1).

I've found what I consider to be a serious bug an issue with ColdFusion's ehache implementation and query objects that may bite you if you're not aware of how it works. If I take a query object and stick it in cache, then perform an operation on the original query object such as adding a column or performing certain query of query operations, ColdFusion is treating the query object that's in cache as if it were a copy by reference to the original query object. That is, if I make a change to the original query object, ColdFusion is also applying the change to the version that's in cache.

As far as I'm concerned, cache represents a boundary that ColdFusion should not implicitly cross. With most caching systems I've worked with in the past (such as memcached), the cache always acted as a dumb key/value store. Unless I perform an explicit cachePut(), I wouldn't expect that CF would update values in the cache. Here's two code snippets that reproduces the case. The first uses the cfartgallery data source:

view plain print about
1<!--- Demo code to show that ColdFusion's passig query values by reference is crossing boundaries and updating a query stored in cache --->
2<cfquery name="getArtists" datasource="cfartgallery">
3select *
4from artists
5</cfquery>
6
7<!--- dump the original query --->
8<cfdump var="#getArtists#">
9
10<!--- put the query in cache --->
11<cfset cachePut("artistsQuery", getArtists,
12'#createTimeSpan(0,0,5,0)#', '#createTimeSpan(0,0,5,0)#')
>

13
14<!--- add a column to the query --->
15<cfset queryAddColumn(getArtists,"newColumn",arrayNew(1))>
16<cfdump var="#getArtists#">
17
18<!--- clear the query --->
19<cfset getArtists="">
20<cfdump var="#getArtists#">
21
22<!--- get the query from cache again - note that the added column is
23there! --->

24<cfdump var="#cacheGet("artistsQuery")#">

The second example shows how this can be done with a query of a query, based on a different bug Ray found previously (see http://www.coldfusionjedi.com/index.cfm/2009/8/28/Another-example-of-the-QofQ-Bug). In this sample, note that the date gets reformatted by the query of a query and both the original query and the cached version get updated:

view plain print about
1<cfset mydata = queryNew("mydate, rannum")>
2
3 <cfloop index="loop1" from="1" to="5">
4 <cfset newrow = queryaddrow(mydata, 1)>
5 <cfset temp = querysetcell(mydata, "mydate", #dateformat(now()-
6loop1,"dd-mmm-yy")#, #loop1#)
>

7 <cfset temp = querysetcell(mydata, "rannum", 55.65, #loop1#)>
8 </cfloop>
9
10<cfdump var="#mydata#">
11
12<cfset cachePut("myQuery", myData, '#createTimeSpan(0,0,5,0)#',
13'#createTimeSpan(0,0,5,0)#')
>

14
15<cfdump var="#mydata#">
16
17<cfquery dbtype="query" name="query4graph">
18 select mydate, rannum from mydata
19 </cfquery>
20
21 <cfdump var="#query4graph#">
22
23 <cfdump var="#cacheGet("myQuery")#">

The work around for this problem is to use the duplicate() function to make a clone of the query object before doing the cachePut(). Although this works, there are other potential consequences from having two copies of the query around, so be careful.

Update: It looks like this is actually expected behavior in Ehcache. Unfortunately, it's not documented in the ColdFusion documentation anywhere, but Ehcache actually has two configurable parameters (as of v. 2.10) called copyOnRead and copyOnWrite that determine whether values returned from the cache are by reference or copies of the original values. By default, items are returned by reference. Unfortunately we can't take advantage of these parameters right now as CF 9.0.1 implements Ehcache 2.0.

I can live with this, but it's not what I expected as I've always viewed Ehcache as a "dumb" key-value store and certainly didn't expect this behavior. Even if ColdFusion was running v 2.10 of Ehcache, it's still not something we could easily configure. Since ColdFusion currently doesn't have a cacheNew() function for creating new user-defined cache regions, the only way to turn this functionality off would be to hard-code your cache configuration in your ehcache.xml file for each user-defined region where you want to disable copyOnRead and copyOnWrite.

Retrieving a List of Active Ehcache Regions in ColdFusion 9.0.1 and a Potential Gotcha

ColdFusion 9.0.1 added a new function called cacheGetSession() that returns the underlying cache object for Ehcache. What's really cool about this is that it allows you to get at just about all of the features available in Ehcache that aren't directly exposed to ColdFusion through built-in functions.

I was recently working on some example code for an upcoming caching presentation when I ran into a situation where I wanted to get a list of all caches that had been created on the server, including custom caches I had created myself. One simple line of code is all it took:

view plain print about
1<cfset cacheNames = cacheGetSession('object').getCacheManager().getCacheNames()>
2
3<cfdump var="#cacheNames#">

What you should note here is that even though I'm telling cacheGetSession() to get the cache object for the default object cache, getCacheManager() exposes a method called getCacheNames() which returns an array of cache names for the instance of cache manager. In the case of ColdFusion's Ehcache implementation, ColdFusion instantiates cacheManager as a singleton such that all caches are managed by the single cache manager. When you call getCacheNames(), it will return you a list of all caches currently implemented on the instance of ColdFusion.

While this may not be an issue for many people, if you are on a shared server it may be a concern. The only way to disable this is to disable the cacheGetSession() function using sandbox security.