Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at BFusion / BFLEX 2010 (Bloomington, Indiana) with: Matt Boles
Ben Nadel at BFusion / BFLEX 2010 (Bloomington, Indiana) with: Matt Boles

The CachedWithin Function Memoization Feature Appears To Compare Complex Objects By Value In Lucee 5.3.2.77

By Ben Nadel on
Tags: ColdFusion

Earlier today, I demonstrated that the cachedWithin Function memoization feature caches the output buffer in Lucee 5.3. While this was an unexpected behavior, I think it makes perfect sense. And, as a quick follow-up post, I wanted to share yet another cachedWithin behavior that wasn't immediately obvious to me: Complex objects, like Structs, Arrays, and Queries, appear to be compared "by value" - not "by reference" - when it comes to the "unique arguments" portion of the Function memoization control flow in Lucee 5.3.2.77.

Coming from the world of JavaScript, where immutable data structures are currently en vogue, the concept of "uniqueness" applies to simple values and object references. Meaning, two separate object references are different even if they aggregate the same values. As such, I just assumed that Lucee's cachedWithin memoization worked the same way.

After some testing, however, it seems that Lucee CFML will actually calculate uniqueness by looking at the contents of complex data structures. That is, for example, if two different Arrays contain the same values, Lucee's cachedWithin feature sees them as the same "input".

To demonstrate this, I created a collection of duplicate object declarations. Then, I looped over that collection and passed the individual values into a cachedWithin Function which, in turn, incremented a counter. If the counter incremented, it meant that the inputs were "new" and the Function was executed; if the counter remained the same, it meant that the inputs were "known" and a cached result was returned:

<cfscript>

	counter = 0;
	
	// I test the caching of Function "outcome" in Lucee. Each execution of the non-
	// cached version will increment the counter above.
	// --
	// NOTE: By using cachedWithin="request", the outcome of this Function will be
	// memoized based on its arguments.
	public any function testCaching() cachedWithin = "request" {

		return( ++counter );

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// This collection contains PAIRS of DUPLICATE COMPLEX VALUES (ie, different
	// REFERENCES that have the same underlying structure).
	complexValues = [
		[],
		[],
		{},
		{},
		[{}],
		[{}],
		[[]],
		[[]],
		[""],
		[""],
		[[],{}],
		[{},[]],
		{a:1},
		{a:1},
		{a:2},
		{a:2},
		{a:3,b:{}},
		{a:3,b:{}},
		{a:3,b:[]},
		{a:3,b:[]},
		{a:{b:1,c:{d:[1,2,3,4,{e:5}]}}},
		{a:{b:1,c:{d:[1,2,3,4,{e:5}]}}},
		deserializeJson( '{"a":{"b":1,"c":{"d":[1,2,3,4,{"e":5}]}}}' ),
		queryNew( "id,name", "varchar,varchar", [["1","Kim"]] ),
		queryNew( "id,name", "varchar,varchar", [["1","Kim"]] ),
		new Simple( "hello" ),
		new Simple2( "hello" )
	];

	for ( value in complexValues ) {

		echo( "<p>#testCaching( value )# ... #serializeJson( value )#</p>" );

	}

</cfscript>

As you can see, every single one of the items in this collection is a unique reference in memory. However, when we run this Lucee ColdFusion code, we can see that the cachedWithin memoization feature comes into play:

Complex objects appear to be compared by value when it comes to cachedWithin Function caching in Lucee 5.3.2.77.

When we look at these results, it becomes clear that "unique object reference" does not drive the uniqueness of arguments when it comes to the cachedWithin memoization feature in Lucee CFML. Even two ColdFusion Component (CFC) instances are considered the "same input" if they aggregate the same public properties.

Coming from the JavaScript world, this value-level evaluation of inputs was not obvious to me. As such, I am assume that it won't be obvious to others. And, that other may find this post helpful.



Reader Comments

Super interesting that [] would be the same as {} in terms of memoization. I can't think of a reason why this would matter, but feels wrong (to me). Does it make sense to you?

My first thought was that Lucee must be memoizing based on the serialized response, but that's clearly not true. ??

Reply to this Comment

Also...thanks for bringing back the videos!

FYI-the ?? in my last comment was the (thinking-emoji). I've been wondering how difficult it would be to create a map that would replace extended characters with their text-based equivalents rather than generalize them all with ??. Have you ever considered that?

Reply to this Comment

@Chris,

Yeah, the {}/[] one is strange. I wonder if it's using some sort of Java hash-code under the hood and these evaluate to the same hash-code? To be honest, I don't really know much about Java.

Re ??, I think the problem is that my blog-code is so old, I never configured the database to support utf8mb4 characters (which include the set of unicode that pertains to most emoji). That should be something I can just update in the database .... will put that on my backlog of site-ideas.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
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.