In Phoenix Next Week? Come See a Preview of My cf.Objective Caching Presentation

If you happen to be in the Phoenix area on Wednesday, January 25th, stop on by the University of Advancing Technology at 6:30 pm to catch a preview of my upcoming cf.Objective presentation: Ten Steps to Move Your Use of Caching Beyond the Basics. Here's a short description of the presentation:

We all know that caching plays an important part of any performant and scalable application architecture and that with ColdFusion 9 caching is easier than ever. This session explores 10 steps you can take to move your use of caching beyond what ColdFusion provides out of the box.

*Please note that this presentation won't be recorded/streamed. You have to be there in person to see it. Normally I'm all for recording and streaming, but since this is a preview of a talk I'll be giving at an upcoming conference, I want to limit the audience out of respect to those attending the conference.

User Defined Caches in Coldfusion 9.0 and 9.0.1

The follow up discussion to a recent blog post by Ray Camden made me realize that there's a lot of misunderstanding (including the ColdFusion documentation) about how ColdFusion's user defined caches work. In this blog post, I hope to clear up some of that confusion.

ColdFusion 9.0 completely overhauled CF's caching mechanism and replaced the old file based system with Ehcache. Out of the box, ColdFusion made two default caches available to every ColdFusion application, an object cache and a template cache. It was also possible to create additional user-defined caches at runtime, but only if you were using the cfcache tag, not the caching functions. Here's an example that puts and gets data from a user defined cache:

view plain print about
1<!--- attempt to get the artist query from the custom object cache --->
2<cfcache
3 key="myCustomObjectCache"
4 action="get"
5 id="artistQuery"
6 name="getArtists"
7 metadata="myMeta">

8
9<!--- if the item isn't there, it'll return null. In that case, run the query
10 and cache the results and rerun the data from the db instead --->

11<cfif isNull(getArtists)>
12 <cfquery name="getArtists" datasource="cfartgallery">
13 SELECT *
14 from artists
15 </cfquery>
16
17 <cfcache
18 key="myCustomObjectCache"
19 action="put"
20 id="artistQuery"
21 value="#getArtists#">

22</cfif>
23
24<!--- dump the query from cache --->
25<cfdump var="#getArtists#">

ColdFusion 9.0.1 added the ability to use user defined caches in many of the caching functions as well. Here's our previous example written using cacheGet() and cachePut() instead of the cfcache tag:

view plain print about
1<!--- attempt to get the artist query from the custom object cache --->
2<cfset getArtists = cacheGet('artistQuery', 'myCustomObjectCache')>
3
4<!--- if the item isn't there, it'll return null. In that case, run the query
5 and cache the results and rerun the data from the db instead --->

6<cfif isNull(getArtists)>
7 <cfquery name="getArtists" datasource="cfartgallery">
8 SELECT *
9 from artists
10 </cfquery>
11
12    <cfset cachePut('artistQuery', '#getArtists#', 15, 15, 'myCustomObjectCache')>
13</cfif>
14
15<!--- dump the query from cache --->
16<cfdump var="#getArtists#">

One thing to note here is that neither cacheGetProperties() nor cacheSetProperties() can be used to configure the properties for a custom cache in ColdFusion 9.0 or 9.0.1. If you want the cache configuration to differ from ColdFusion's default cache configuration, you'll need to configure the cache in your ehcache.xml file:

view plain print about
1<cache
2 name="myCustomObjectCache"
3 maxElementsInMemory="10000"
4 eternal="false"
5 timeToIdleSeconds="86400"
6 timeToLiveSeconds="86400"
7 overflowToDisk="false"
8 diskSpoolBufferSizeMB="30"
9 maxElementsOnDisk="10000000"
10 diskPersistent="false"
11 diskExpiryThreadIntervalSeconds="3600"
12 memoryStoreEvictionPolicy="LRU"
13 clearOnFlush="true"
14/>

Obviously it's not always desirable or even possible (say in the case of a shared hosting environment) to hard code configuration settings in the ehcache.xml file. Besides that, anytime you make changes to the XML file you need to restart ColdFusion for them to take effect. While I hope that Adobe adds the ability to set properties for custom caches in a future release of ColdFusion, there is a solution you can take advantage of today in the form of a small UDF I wrote called cacheCreate().

The cacheCreate() UDF allows you to create a new user defined cache at runtime and pass in configuration options that ColdFusion currently requires you to set in your ehcache.xml file. It uses Java to call the underlying Ehcache API and do the heavy lifting for you. Here's the code (you can also get cacheCreate() at cflib.org:

view plain print about
1<cffunction name="cacheCreate" output="false" returntype="void"
2    hint="I create a new user defined cache region in Ehcache"
3    description="I create a new user defined cache region in Ehcache. This function
4             allows you to also configure the attributes for the custom cache,
5             something you would normally have to hard code in the ehcache.xml
6             file if you rely on ColdFusion'
s built in caching functions. I named
7             the function cacheCreate() and not cacheNew() in the hopes that a
8             future version of ColdFusion includes a cacheNew() function with
9             similar functionality.">
10
11    <!--- this is what's configurable as of Ehcache 2.0 (CF 9.0.1). Only required
12         argument is Name --->

13    <cfargument name="
name" type="string" required="true">
14    <cfargument name="
maxElementsInMemory" type="numeric" default="10000">
15    <cfargument name="
maxElementsOnDisk" type="numeric" default="10000000">
16    <cfargument name="
memoryStoreEvictionPolicy" type="string" default="LRU">
17    <cfargument name="
clearOnFlush" type="boolean" default="true">
18    <cfargument name="
eternal" type="boolean" default="false">
19    <cfargument name="
timeToIdleSeconds" type="numeric" default="86400">
20    <cfargument name="
timeToLiveSeconds" type="numeric" default="86400">
21    <cfargument name="
overflowToDisk" type="boolean" default="false">
22    <cfargument name="
diskPersistent" type="boolean" default="false">
23    <cfargument name="
diskSpoolBufferSizeMB" type="numeric" default="30">
24    <cfargument name="
diskAccessStripes" type="numeric" default="1">
25    <cfargument name="
diskExpiryThreadIntervalSeconds" type="numeric" default="120">
26    
27    <!--- We need to do this in java because ColdFusion's cacheGetSession() returns
28     the underlying object for an EXISTING cache, not the generic cache manager --->

29    <cfset local.cacheManager = createObject('java', 'net.sf.ehcache.CacheManager').getInstance()>

30
31    <!--- constructor takes cache name and max elements in memory --->
32    <cfset local.cacheConfig = createObject("java", "net.sf.ehcache.config.CacheConfiguration").init("#arguments.name#", #arguments.maxElementsInMemory#)>

33    <cfset local.cacheConfig.maxElementsOnDisk(#arguments.maxElementsOnDisk#)>

34    <cfset local.cacheConfig.memoryStoreEvictionPolicy("#arguments.memoryStoreEvictionPolicy#")>
35    <cfset local.cacheConfig.clearOnFlush(#arguments.clearOnFlush#)>
36    <cfset local.cacheConfig.eternal(#arguments.eternal#)>
37    <cfset local.cacheConfig.timeToIdleSeconds(#arguments.timeToIdleSeconds#)>
38    <cfset local.cacheConfig.timeToLiveSeconds(#arguments.timeToLiveSeconds#)>
39    <cfset local.cacheConfig.overflowToDisk(#arguments.overflowToDisk#)>
40    <cfset local.cacheConfig.diskPersistent(#arguments.diskPersistent#)>
41    <cfset local.cacheConfig.diskSpoolBufferSizeMB(#arguments.diskSpoolBufferSizeMB#)>
42    <cfset local.cacheConfig.diskAccessStripes(#arguments.diskAccessStripes#)>
43    <cfset local.cacheConfig.diskExpiryThreadIntervalSeconds(#arguments.diskExpiryThreadIntervalSeconds#)>
44
45    <cfset local.cache = createObject("java", "net.sf.ehcache.Cache").init(local.cacheConfig)>
46    <cfset local.cacheManager.addCache(local.cache)>
47</cffunction>

To create and configure a new user defined cache in your application, all you need to do is something like this:

view plain print about
1<!--- let's build a struct of arguments. It's much easier to pass this way --->
2<cfset myProps = structNew()>
3<cfset myProps.name = "myCustomCache">
4<cfset myProps.maxElementsInMemory = 10>
5<cfset myProps.maxElementsOnDisk = 10>
6<cfset myProps.memoryStoreEvictionPolicy = "FIFO">
7<cfset myProps.clearOnFlush = true>
8<cfset myProps.eternal = false>
9<cfset myProps.timeToIdleSeconds = 86400>
10<cfset myProps.timeToLiveSeconds = 86400>
11<cfset myProps.overflowToDisk = false>
12<cfset myProps.diskPersistent = true>
13<cfset myProps.diskSpoolBufferSizeMB = 30>
14<cfset myProps.diskAccessStripes = 1>
15<cfset myProps.diskExpiryThreadIntervalSeconds = 120>
16
17<!--- create the new custom cache region --->
18<cfset cacheCreate(argumentCollection=myProps)>
19
20<!--- prove that it's there the CF way --->
21<cfdump var="#cacheGetSession('myCustomCache', 'true').getCacheManager().getCacheNames()#">

While this won't work in all cases (say you want to cluster your cache or add in a Terracotta Server Array), it should get you thinking about other ways you can extend ColdFusion's ehcache implementation by utilizing some of the Ehcache Java API within CF.

Slides, Config and Code from my cf.objective() 2011 Presentation

Slides, config files and code for my cf.objective() 2011 presentation, "Undocumented and Off Script: ColdFusion & Ehcache" are now available. You can view the slides inline, or download the attached zip file which contains the presentation and all files/code from the presentations.

As always, if you have questions or want to discuss, leave a comment or hit me up on twitter at @styggiti.

Speaking at cf.Objective 2011: Undocumented and Off Script: ColdFusion & Ehcache

Headed to cf.Objective 2011 and want to learn more about caching in ColdFusion with Ehcache? Stop by my session Undocumented and Off Script: ColdFusion & Ehcache on Thursday, May 12th at 10:15 am. I'll cover a broad range of topics including upgrading the version of Ehcache that ships with ColdFusion, clustered caching, distributed caching, cache search, cache monitoring, developer tools, and much, much more.

I hope to see you there!

Building High Performance Applications with ColdFusion 9 and Ehcache 2.4 on DZone

My latest article on Building High Performance Applications with ColdFusion 9 and Ehcache 2.4 is out on DZone. In addition to covering basic caching concepts, the article also walks you through the process of updating the version of Ehcache that ships with Coldfusion from 2.0 to 2.4 as well as installing and configuring a Terracotta server for distributed caching.

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.

Announcement: Boosting Performance and Scale for ColdFusion Applications with Ehcache (Webinar)

If you've been following my series on caching in ColdFusion 9, you know that ColdFusion 9.0 includes the powerful caching platform Ehcache from Terracotta. What you may not know, though, is that in ColdFusion 9.0.1 Adobe upgraded the Ehcache engine from version 1.6 to 2.0. This brings several new caching capabilities to ColdFusion and makes them dead simple to implement. If you're interested in learning more, Mike Allen from Terracotta and I are presenting a webinar on August 12, 2010 at 11am Pacific time. Here's the official announcement:

Join me and Mike Allen, the head of product management at Terracotta in a discussion about simple and effective ways to scale and boost the performance of ColdFusion applications using Ehcache.

In this webcast, you'll learn:

  • Why caching is important
  • How ColdFusion leverages Ehcache to boost performance
  • Tips and tricks to leverage the newest features of Ehcache in ColdFusion
  • How to take advantage of the ability to snap in Enterprise Ehcache to scale out your applications

Please register for this webcast even if you are unable to attend and you will get an email with a link to the recording.

Register for the webinar

ColdFusion 9 Caching Bugs/Features I'd Like to See Addressed in ColdFusion 9.0.1 or Updater 1

Now that ColdFusion 9 has been out for a few months, I've compiled a list of issues around caching that I'd like to see addressed in ColdFusion 9.0.1 or Updater 1. These are all issues that haven't already been discussed elsewhere or filed by others as bugs/enhancement requests.

To get the ball rolling, I'm listing each of the issues here along with the bug/enhancement number for the corresponding issue I filed in Adobe's ColdFusion Bug Database.

I have some additional things I'd like to see such as cache cluster configuration withing the CF Admin and an upgrade to Ehcache EX so that we can do true distributed caching within ColdFusion, but I'll address those items in a later post. For now, here's my list. I'm interested to hear how others are doing with the new caching in ColdFusion 9.

Ambiguous Error Message when Distributed Caching via RMI is Enabled and Server is not Connected to a Network (Bug 81840)

If you've configured your ehcache.xml file for clustering via RMI and you are not connected to a network, ColdFusion will throw an error if you try to perform any caching activities. This bit me at MAX when my network connection dropped before I started showing my examples. When I tried running any of my examples, I got the following error:

view plain print about
1The web site you are accessing has experienced an unexpected error.
2Please contact the website administrator.
3The following information is meant for the website developer for debugging purposes.
4
5Error Occurred While Processing Request
6error setting options
7     
8The error occurred in C:\_web\default\wwwroot\MAX2009\cache_get_put_function.cfm: line 1
91 : <cfset getArtists = cacheGet("artistQuery")>
102 :
113 :
12________________________________________
13Resources:
14"    Check the ColdFusion documentation to verify that you are using the correct syntax.
15"
    Search the Knowledge Base to find a solution to your problem.
16
17Browser     Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
18Remote Address     127.0.0.1
19Referrer     http://localhost/max2009/
20Date/Time     23-Oct-09 11:26 AM
21
22Stack Trace
23at cfcache_get_put_function2ecfm561514261.runPage(C:\_web\default\wwwroot\MAX2009\cache_get_put_function.cfm:1)
24net.sf.ehcache.CacheException: error setting options
25    at net.sf.ehcache.distribution.MulticastRMICacheManagerPeerProvider.init(MulticastRMICacheManagerPeerProvider.java:93)
26    at net.sf.ehcache.CacheManager.init(CacheManager.java:241)
27    at net.sf.ehcache.CacheManager.<init>(CacheManager.java:221)
28    at net.sf.ehcache.CacheManager.create(CacheManager.java:415)
29    at net.sf.ehcache.CacheManager.getInstance(CacheManager.java:436)
30    at coldfusion.tagext.io.cache.ehcache.GenericEhcache.createCache(GenericEhcache.java:294)
31    at coldfusion.tagext.io.cache.ehcache.GenericEhcache._getCache(GenericEhcache.java:288)
32    at coldfusion.tagext.io.cache.ehcache.GenericEhcache.getCache(GenericEhcache.java:255)
33    at coldfusion.tagext.io.cache.ehcache.GenericEhcache.get(GenericEhcache.java:72)
34    at coldfusion.tagext.io.cache.CacheTagHelper.getFromCache(CacheTagHelper.java:226)
35    at coldfusion.runtime.CFPage.CacheGet(CFPage.java:8025)
36    at cfcache_get_put_function2ecfm561514261.runPage(C:\_web\default\wwwroot\MAX2009\cache_get_put_function.cfm:1)
37    at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:231)
38    at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:416)
39    at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
40    at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:342)
41    at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48)
42    at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
43    at coldfusion.filter.PathFilter.invoke(PathFilter.java:87)
44    at coldfusion.filter.LicenseFilter.invoke(LicenseFilter.java:27)
45    at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:70)
46    at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:74)
47    at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
48    at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
49    at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46)
50    at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
51    at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
52    at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:53)
53    at coldfusion.CfmServlet.service(CfmServlet.java:200)
54    at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
55    at jrun.servlet.FilterChain.doFilter(FilterChain.java:86)
56    at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
57    at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
58    at jrun.servlet.FilterChain.doFilter(FilterChain.java:94)
59    at jrun.servlet.FilterChain.service(FilterChain.java:101)
60    at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106)
61    at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
62    at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286)
63    at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543)
64    at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203)
65    at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428)
66    at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)

This error only happens if you have RMI enabled in ehcache.xml and you aren't connected to a network. I don't expect this would ever be a problem in a production environment, but it could pop up on development machines – especially if people develop disconnected. At a minimum, I'd like to potentially see a more meaningful error message (even though you can narrow it down to RMI via the stack trace).

DiskStore Configuration is only Configurable in ehcache.xml (Bug 81841)

In ehcache.xml, there's a configurable property called diskStore that's set by default to point to your Java temp directory:

view plain print about
1<diskStore path="java.io.tmpdir"/>

You can change this to any path you want in ehcache.xml and it applies globally.

The ColdFusion documentation for the cacheSetProperties() tag currently shows that diskstore is a valid option:

http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7c18.html

My testing, however, has shown that is not working in the shipping build of ColdFusion 9. Try the following example (you'll need to create a directory off your root, c:/temp):

view plain print about
1<cfset myProps = structNew()>
2<cfset myProps.diskstore = "c:/temp"> <!--- in the docs, but not currently implemented --->
3<cfset myProps.diskpersistent = "true">
4<cfset myProps.eternal = "false">
5<cfset myProps.maxelementsinmemory = "5000">
6<cfset myProps.maxelementsondisk = "100000">
7<cfset myProps.memoryevictionpolicy = "LRU">
8<cfset myProps.objecttype = "Object">
9<cfset myProps.overflowtodisk = "true">
10<cfset myProps.timetoidoleconds = "86400">
11<cfset myProps.timetolivesecond = "86400">

Before:

view plain print about
1<cfdump var="#cacheGetProperties("Object")#">
2
3<!--- update the cache properties --->
4<cfset cacheSetProperties(myProps)>

After:

view plain print about
1<cfdump var="#cacheGetProperties("Object")#">
2
3<cfset cachePut("item1", "this is a test")>

If you run this code and check the c:/temp directory you should find it empty. Check your jave temp directory (c:/windows/temp on wintel) and you'll see that the files are still written there showing that the default value in ehcache.xml is being used and not the value being set in the cacheSetProperties() function.

The obvious action is to remove this attribute from the documentation until it can be implemented in a later version of ColdFusion. I would like to see it implemented, though, as I think it's useful to be able to set the diskstore location programmatically.

CacheGetMetadata() Returns both Cache Wide and Item Specific Metadata (ER 81842)

Currently, the cacheGetMetadata() function returns metadata that applies both to the individual cache item passed to the function as well as metadata for the entire cache region that the cached item came from. For example, if you have an item in the cache with an ID of "item1" and you can get the metadata for it like this:

view plain print about
1<cfdump var="#cacheGetMetadata("item1")#">

What this actually returns to you is a structure with two sets of information:

Cache_hitcount and cache_misscount apply to the overall cache. In other words, how many hits and misses the entire cache have received. The rest of the keys returned in the structure apply to the item passed in to the cacheGetMetadata() function.

I see a couple of potential issues with how this has been implemented. What I think we really need is two separate functions here. The existing cacheGetMetadata() should return just the metadata that is specific to the item passed to the function, not metadata on the entire cache. I would expect a separate function to retrieve metadata for the cache itself. To avoid confusion, I'd call the new function something like cacheGetStats(). In my mind it would return the cache_hitcount and cache_misscount that are currently returned by the cacheGetMetadata() function. It would also return a whole lot more that's available from ehcache but not exposed to ColdFusion – things like the total number of items currently in the cache, total size in bytes of all of the items in the cache, etc.

An Alternate Proposal for Additional ehcache Functionality (ER 81843)

What I think might be an even better and more future-proof solution for obtaining additional metadata (and more) from ehcache would be to provide similar functions to the ORMgetSessionFactory() and ORMgetSession() functions. If we had something like cacheGetSessionFactory() and/or cacheGetSession(), we could get access to all of the additional functionality in ehcache that's not currently exposed. Specifically this would give us an easy to get access to more cache statistics than we can access now, without having to write a bunch of new ColdFusion functions to handle things that might not be considered core or essential to caching.

Inability to Specify Cache Key in Functions (ER 81844)

The cfcache tag has the key attribute which allows you to specify a custom cache region other than the default template or object cache:

view plain print about
1<cfcache key="customCache" action="put" id="1" value="#now()#">

This creates a cache region at runtime called customCache. Getting, putting and flushing the cache are all supported from the cfcache tag. However, it's not currently possible to use any of the caching functions to operate on the custom cache region because there is no way to specify which cache region you want to use – the functions always apply to the default object or template cache.

I'd like to propose adding an optional attribute to all cache functions to allow a developer to pass in a custom cache region (key) for the function to operate against where applicable.

Naming Inconsistencies for Removing Cache Items (81845)

There are some naming inconsistencies between the cfcache tag and the new caching functions. To remove an item from the object cache using the cfcache tag, you use action="flush" like this:

view plain print about
1<cfcache action="flush" id="itemID">

Using a function instead, you use cacheRemove():

view plain print about
1<cfset cacheRemove("itemID")>

Fragment Cache Gotcha (bug 81846)

I was just playing around some more with the template cache in ColdFusion 9 and noticed a behavior I hadn't expected that I think could potentially cause problems for people down the road.

Consider the following example:

view plain print about
1<cfcache>
2This is a fragment
3</cfcache>
4
5<cfcache>
6<cfoutput>
7And so is this #now()#
8</cfoutput>
9</cfcache>
10
11<cfdump var="#getalltemplatecacheids()#">

If you execute this, you'll get some output as well as a dump of all of the cached items in the template cache (getalltemplatecacheids is an undocumented function). At this point, there are two items in the cache:

Now say you weren't happy that the two lines of output on your page were all run together and you wanted to put a line break between each one. You might modify your code and add in a simple paragraph tag or line break like so:

view plain print about
1<cfcache>
2This is a fragment
3</cfcache>
4
5<p>
6
7<cfcache>
8<cfoutput>
9And so is this #now()#
10</cfoutput>
11</cfcache>
12
13<cfdump var="#getalltemplatecacheids()#">

Go ahead and run the code once you've inserted the <p> tag. What you'll now see is that you have three items in the cache:

What's happening is that ColdFusion is using the position of the code in your page as part of the ID for the cached item. When you inserted the <p> tag, the position of the fragment you cached also changed by moving down a few lines, and ColdFusion assumed you had added a new page fragment that you wanted to cache.

This may or may not be a big deal to people as CF will still pull the correct cached item every time. The gotcha is that if you have a lot of caching going on, and you're making a lot of changes in a development environment, it's possible to fill your cache up with a lot of junk pretty quickly.

I'm not making any statements on how this feature was implemented - I just want to make people aware of how the template cache works under the covers since it's not documented and if people are paying attention when they are developing they might see some things with the template cache that don't make sense if they don't know how it works.

cacheSetProperties() and cacheGetProperties() are Missing Configurable Parameters (ER 81847)

ehcache lets you configure a number of parameters for a cache region via the ehcache.xml file. ColdFusion exposes most of these configurable parameters at runtime using cacheSetProperties() and cacheGetProperties(). There are, however, 3 parameters which currently aren't exposed and can only be set within ehcache.xml:

diskSpoolBufferSizeMB: This is the size to allocate the DiskStore for a spool buffer. Writes are made to this area and then asynchronously written to disk. The default size is 30MB. Each spool buffer is used only by its cache. If you get OutOfMemory errors consider lowering this value. To improve DiskStore performance consider increasing it. Trace level logging in the DiskStore will show if put back ups are occurring.

clearOnFlush: It determines whether the MemoryStore should be cleared when flush() is called on the cache. By default, the MemoryStore is cleared. Useful is you want to back up a cache to the file system without clearing the MemoryStore.

diskExpiryThreadIntervalSeconds: The number of seconds between runs of the disk expiry thread. The default value is 120 seconds.

I'm not sure why these were left out but it would be nice if they were also included in the configurable properties using ColdFusion functions.