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

Posted July 22, 2009 at 8:58 AM by Ben Nadel

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

  • <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:

  • <!--- 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:

  • <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.


You Might Also Be Interested In:



Reader Comments

Jul 22, 2009 at 9:11 AM // reply »
10,640 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 »
113 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 »
16 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 »
10,640 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.


Aug 19, 2010 at 4:05 PM // reply »
4 Comments

Undoubtedly you've put two and two together already but I'd guess that the source of this bug is the caching by reference bug (undocumented feature) that Rob Brooks-Bilson talks about here: http://www.brooks-bilson.com/blogs/rob/index.cfm/2010/8/9/Bug-with-Ehcache-and-ColdFusion-Query-Objects


Aug 21, 2010 at 3:29 PM // reply »
10,640 Comments

@Howard,

That's an interesting behavior that Rob is pointing out. I am not sure if it related, however. I think that mine has more to do with the meta data of the file and the trusted cache (theory). When a function gets created, ColdFusion creates meta data that gets associated with that function. When I delete the file, it's probably deleting the meta data, which I think, is messing up the function invocation.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 12, 2012 at 3:37 AM
Learning ColdFusion 8: CFImage Part III - Watermarks And Transparency
Hi Ben, Just to ask currently it is placed bottom right corner, if i need to replace the same rendered image on the bottom left side or in the bottom center, how that can be calculated. bottom ce ... read »
Feb 11, 2012 at 9:29 PM
Use jQuery's SlideDown() With Fixed-Width Elements To Prevent Jumping
I can't say how glad I am that I found your post. Thank you very much. ... read »
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »