Skip to main content
Ben Nadel at cf.Objective() 2017 (Washington, D.C.) with: Zachary Brooks
Ben Nadel at cf.Objective() 2017 (Washington, D.C.) with: Zachary Brooks

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

By on
Tags:

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.

Want to use code from this post? Check out the license.

Reader Comments

205 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. ??

205 Comments

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?

15,674 Comments

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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel