Learning ColdFusion 9: A Bug With File-Based Object Caching

Posted July 22, 2009 at 8:58 AM

Tags: ColdFusion

I found a weird bug in ColdFusion 9's new caching system. It seems that when you cache an object that is based on a file, if you delete the file while the object is still in the cache, the cache becomes corrupt. To demonstrate this, I have created a ColdFusion user defined function that simply grabs a reference to itself from the system cache (based on the method name) and tries to update its own meta data:

Clicker.cfm

 Launch code in new window » Download code as text file »

  • <cffunction
  • name="clicker"
  • access="public"
  • returntype="numeric"
  • output="false"
  • hint="I keep track of how many times I was clicked.">
  •  
  • <!--- Get the refernce to this method from the cache. --->
  • <cfset local.this = cacheGet( "clicker" ) />
  •  
  • <!--- Get the meta data for this method. --->
  • <cfset local.metaData = getMetaData( local.this ) />
  •  
  • <!--- Param the click count to be zero. --->
  • <cfparam
  • name="local.metaData.clickCount"
  • type="numeric"
  • default="0"
  • />
  •  
  • <!---
  • Increment the click count and return the current click
  • count on this method.
  • --->
  • <cfreturn ++local.metaData.clickCount />
  • </cffunction>

As you can see, this method, "clicker", assumes that its reference was cached in the system cache at the ID, "clicker". Using this ID, it then grabs a reference to itself, params a click count variable, increments the variable, and then returns the current click count. Since ColdFusion function objects are first-class citizens, there should be nothing questionable about this.

Then, I created another ColdFusion page that uses the above UDF. However, rather than including it directly, it reads in the above UDF file as plain text and writes it to ColdFusion 9's new virtual file system (RAM disk). Then, the UDF gets included from the RAM disk, making the Clicker() method available in the page's variables scope. This method instance is then cached in the system cache and the RAM disk is cleaned up:

 Launch code in new window » Download code as text file »

  • <!--- Read in the clicker file. --->
  • <cfset clickerCode = fileRead(
  • expandPath( "./clicker.cfm" )
  • ) />
  •  
  • <!---
  • Write the clicker ColdFusion function code to teh RAM disk
  • so that we can then include it into the current file.
  • --->
  • <cfset fileWrite(
  • "ram://clicker.cfm",
  • clickerCode
  • ) />
  •  
  • <!---
  • Include the RAM-based clicker method into the current page.
  • This will make the method "clicker" available to the variables
  • scope of this page.
  •  
  • NOTE: We have a mapping to the ram disk as "/ram".
  • --->
  • <cfinclude template="/ram/clicker.cfm" />
  •  
  • <!--- Get a reference to the first-class method object. --->
  • <cfset clickerMethod = clicker />
  •  
  • <!---
  • Add the clicker method to system cache so that the clicker
  • method can refer to itself internally.
  • --->
  • <cfset cachePut(
  • "clicker",
  • clickerMethod,
  • createTimeSpan( 0, 0, 5, 0 ),
  • createTimeSpan( 0, 0, 5, 0 )
  • ) />
  •  
  • <!---
  • Now that we have included the clicker method and have a handle
  • on the function object, we can delete the code from the RAM.
  • --->
  • <cfset fileDelete( "ram://clicker.cfm" ) />
  •  
  • <!---
  • Execute the clicker method a few times to see that it
  • is successfully working - retrieving itself from the system
  • cache and updating its own meta data.
  • --->
  • <cfoutput>
  • Clicked: #clicker()#<br />
  • Clicked: #clicker()#<br />
  • Clicked: #clicker()#<br />
  • Clicked: #clicker()#<br />
  • Clicked: #clicker()#<br />
  • </cfoutput>

As you can see, once we have a handle on the clicker Function object, we store it for 5 minutes in the system cache and delete the scratch code file from the RAM disk. Since ColdFusion treats Function objects as first-class citizens, there should be nothing odd about this and nothing that requires the Function object to have a corresponding code file. However, when we run the code above, rather than seeing the click count output, we get the following ColdFusion error:

An error occurred when performing a file operation lastModified on file /clicker.cfm. The cause of this exception was: org.apache.commons.vfs.FileSystemException: Could not determine the last modified timestamp of "ram:///clicker.cfm" because it does not exist.

If I go back and comment out this line:

 Launch code in new window » Download code as text file »

  • <cfset fileDelete( "ram://clicker.cfm" ) />

... then everything works fine and when we run the page, we get the following output:

Clicked: 1
Clicked: 2
Clicked: 3
Clicked: 4
Clicked: 5

It seems that there is a bug in the ColdFusion 9 cache system where the cached objects need to correspond to a physical files. Because Functions and Components are so dynamic at runtime and can be manipulated to be completely different than their original definitions, it seems wrong that any physical file would be required for simple caching.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page


You Might Also Be Interested In:



Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Jul 22, 2009 at 9:11 AM // reply »
7,572 Comments

I have logged this bug:

http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=78981


Jul 22, 2009 at 11:41 AM // reply »
86 Comments

@Ben,

This "bug" appears in other places at least as early as ColdFusion 8 (the only version with which I am familiar).

One example I have run into sometimes. Suppose you have a <cfinterface> and a <cfcomponent> implementing that interface. Suppose you create an instance of the component and cache the instance somewhere, then alter the interface file so that the interface and component definitions are no longer in sync, then attempt to use that instance later on. Boom!

Is this a bug? Good question. I'm going to say - no. Certainly, you would like ColdFusion to work differently. So that makes this a feature request.

On another note, there are a number of languages with first-class functions, including: JavaScript, Python, Ruby, Haskell, C#. In fact, first-class functions is central to each of these languages. And it's in part because of their support for first-class functions that I consider these to be stellar languages.

ColdFusion is not among these languages, and neither is Java. Neither ColdFusion nor Java has native support for first-class functions (although some other languages that run on the JVM do support first-class functions).

Functions-as-first-class-objects means "inner anonymous bound closures".
- "Inner": these can be defined within other functions.
- "Anonymous": these need not be named, but may instead simply be assigned to variables as values directly (or passed directly into function calls, or returned directly from the function call they were defined in). Note that this is not necessarily a requirement, but is extraordinarily convenient (Python lacks this).
- "Bound": if these are defined in instance methods, then these must as well be instance methods (applies to object-oriented languages only).
- "Closures": these inner functions must capture as much of the lexical scope (e.g. function-local variables) as will be required - and must still be able to access and modify the captured portion of the lexical scope even after the original function has stopped executing.

ColdFusion function objects, like C function objects, fail to meet all four of these categories. Therefore I would certainly not call ColdFusion functions "first-class."


Jul 22, 2009 at 7:00 PM // reply »
13 Comments

Ben, what happens when you turn on trusted cache? What if the temp file contained a cfcomponent tag and you created and cached an instance of that component instead of just a reference to the UDF?


Jul 27, 2009 at 1:36 PM // reply »
7,572 Comments

@Justice,

Yeah, it would be awesome to get lexical binding. That would make life very awesome. The rest of the points do, to a certain extend, work. You can define functions within functions (as long as they are included). You can assign a function to a variable, but its initial definition cannot be anonymous. And, methods are bound to CFCs only when they are called as methods of the CFC (not executing a reference to a method in a CFC).

Obviously, these are not perfect, but adding lexical binding would be the cats pajamas.

Also, Adobe has confirmed that this is a bug (or a feature request) and has alerted me that it has been fixed for production.

@Brad,

I am not sure what would happen with CFCs. I'll see if I can play around with it.


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 20, 2010 at 9:00 AM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
I would like to say thx for an easy way to create a bottom bar. I do have a ?. Is it possible to center the bar if i want to resize it to ex 85%. Regards Offenbach ... read »
Mar 19, 2010 at 7:26 PM
MySQL 3/4 - com.mysql.jdbc.Driver And allowMultiQueries=true
Thank you very much for this post. Adding allowMultiQueries="true" in context.xml didn't help until I added it to url as allowMultiQueries=true Good idea is to use prepared statements and it will he ... read »
Jim
Mar 19, 2010 at 4:49 PM
Nobody Puts Baby In The Corner!
Wow. This is like suddenly finding a support group for your secret shame. I'm not alone! I always liked this movie, even though it is extremely cheesy. I just wish Jennifer Grey hadn't gotten the ... read »
Mar 19, 2010 at 4:47 PM
Application.cfc OnRequest() Method Affects OnError() Arguments
@Jason and @Ben, I've been doing some CF9 refactoring on our systems and noticed an odd occurrence with onError as well. Found a way to work around my problem, but what I saw was... Background: Our ... read »
Jim
Mar 19, 2010 at 4:44 PM
Shoot 'Em Up Starring Clive Owen And Paul Giamatti
I actually enjoyed this movie quite a lot. It was different, certainly, but I think they were going for more of a Quentin Tarentino-"wow, that was weird"-vibe than an actual spoof. Once I realize ... read »
Mar 19, 2010 at 4:34 PM
An Intensive Exploration Of jQuery With Ben Nadel (Video Presentation)
Hey I guess the video is down. Is there anyway you can upload to youtube or vimeo or some other service? Greatly appreciated. ... read »
Mar 19, 2010 at 4:24 PM
ColdFusion CFPOP - My First Look
@Ben Thanks for the follow up! The root of the problem had to do with being able to trace bounced emails to specific records in a DB table. Let's say you run an email campaign and you get 1,000 bou ... read »
Mar 19, 2010 at 4:15 PM
SQL COUNT( NULLIF( .. ) ) Is Totally Awesome
Thank you Ben and Tony! Either of these work for the summary report I am working on and the info is much appreciated! I think I like Tony's a little better because I won't have to educate every ... read »