cfc's in shared scopes / caching cfc's

17 views
Skip to first unread message

redtopia

unread,
Feb 9, 2010, 5:06:04 PM2/9/10
to CFCDev
I'm using the Application scope to cache "type information" about the
various database objects in my application in order to speed up
database access. Each type has a corresponding "type handler" cfc that
does all the getting/setting/deleting of a specific type of object in
the database.

At first, I was creating the "type handler" cfc object (using
createObject ()) every time I wanted to access a database object, but
I found that createObject is very slow. Now I create the "type
handler" object once and store it in the "type information" that is
cached in the Application scope. So the "type handler" cfc's are now
being cached after I create it.

In order to avoid having to lock my application scope for the entire
time it takes to access the database, I lock the application scope,
copy the "type handler" cfc to the caller's scope using the duplicate
() function, and then make method calls into the copied version of the
"type handler" cfc.

Is there a better way to do this?

I've got a several other instances where I'm unsure about the best
method for accessing data and cfc's that are cached in the Application
scope without having to lock everything all the time. Another example
is that I create a siteMap cfc object that contains a hierarchical
array-based structure that is accessed on just about every request. I
do things like... display links to pages within the current section of
the site. I've got a method called "getSiteMapArray ()" that returns
the site map array. I haven't been calling the Duplicate () function
on the array before returning it... does anyone know if that might be
a source of a memory leak?

Brian Kotek

unread,
Feb 11, 2010, 10:18:29 PM2/11/10
to cfc...@googlegroups.com
As long as you var-scope any method-local variables, and the CFC doesn't have any instance data (data in the variable or this scope) changed during the method calls, you don't need to lock these.


--
You received this message because you are subscribed to the Google Groups "CFCDev" group.
To post to this group, send email to cfc...@googlegroups.com.
To unsubscribe from this group, send email to cfcdev+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/cfcdev?hl=en.


redtopia

unread,
Feb 16, 2010, 11:52:36 AM2/16/10
to CFCDev
However, if any of said components can be modified, then they'll need
to be locked.

Here's a very trimmed down example of a memory leak that I still don't
understand... (excuse syntax errors, i've never run this code)... I
don't know why the JRE can't handle it, but it can't. Look at the
method: "getArray ()" If I simply return this.theArray, it causes jrun
to become extremely unstable and it will eventually fail to return any
pages, or even errors. It will simply stop responding to requests and
the jrun process seems to hang. If I return: duplicate
(this.theArray), my server stabilizes and returns back to normal...
always responding to requests.

It makes me completely rethink how I'm passing arrays around. Could
there be other leaks in my code?

<!--------------------- define the component ("theCFC")
------------------->
<cfcomponent name="theCFC">

<cfset this.theArray = ArrayNew (1)>

<cffunction name="getArray" access="public" output="no"
returnType="Array">
<cfreturn this.theArray> <!--- return using: duplicate
(this.theArray); solves the problem - JRE and server become stable
again --->
</cffunction>

<cffunction name="init" access="public" output="no"
returntype="theCFC">

<!--- generate an array of structs with nested arrays of structs ---
>

<cfloop from="1" to="1000" index="ix">
<cfset s1 = StructNew ()>
<cfset s1.ix = ix>
<cfset s1.theArray = ArrayNew (1)>
<cfloop from="1" to="1000" index="ixx">
<cfset s2 = StructNew ()>
<cfset s2.ix = ixx;
<cfset s2.name = "this is #ixx#">
<cfset ArrayAppend (s1.theArray, s2)>
</cfloop>
<cfset ArrayAppend (this.theArray, s1)>
</cfloop>

<cfreturn this />

</cffunction>

</cfcomponent>

<!------------------- somewhere to initialize -------------------------
>
<cflock scope="application" type="exclusive" throwontimeout="true"
timeout="30">
<cfset Application.theCFC = createObject ("component",
"theCFC").init ()>
</cflock>

<!------------------- the caller creates the memory leak
-------------------->

<cflock scope="application" type="readonly" throwontimeout="true"
timeout="30">
<cfset theArray = Application.theCFC.getArray ()>
</cflock>

<!--- use the array for whatever - somehow JRE GC loses track of local
var "theArray" --->

MEMORY LEAK


On Feb 11, 8:18 pm, Brian Kotek <brian...@gmail.com> wrote:
> As long as you var-scope any method-local variables, and the CFC doesn't
> have any instance data (data in the variable or this scope) changed during
> the method calls, you don't need to lock these.
>

> > cfcdev+un...@googlegroups.com<cfcdev%2Bunsu...@googlegroups.com>

Brian Kotek

unread,
Feb 24, 2010, 7:31:07 PM2/24/10
to cfc...@googlegroups.com
Well the first thing I see is that you're not var-scoping your method-local variables.

Aside from that, I don't really understand what you're trying to do here. You have a CFC with an array held as instance data. That array is populated in the constructor. But then you have code that creates a new instance, and then some (other?) code that gets the array from that instance. And what exactly is the problem? If you keep reloading the page, something happens, or doesn't happen?

Also keep in mind that arrays are passed by VALUE and not by reference.

The problem is that the example you're showing doesn't tell me anything about what you actually want to do. It might be better if you try to explain the problem you're trying to solve and the reason you want to load a CFC into the application scope and then make updates to its instance data.

Regards,

Brian


To unsubscribe from this group, send email to cfcdev+un...@googlegroups.com.

Baz

unread,
Feb 24, 2010, 11:34:48 PM2/24/10
to cfc...@googlegroups.com
Brian is leading down the right path, especially about not needing to lock reads, but just as a quick side-note, you could think about using named locks when you write, so that only that specific transaction is locked and not the entire scope.

Baz

redtopia

unread,
Feb 25, 2010, 12:39:20 PM2/25/10
to CFCDev
You're right... I didn't var scope those variables, which I would do
in a real world example. By the way, what happens if you don't var
scope your local variables?

The example code above represents a site map. I have a CFC that I
store in my application scope that builds and stores a site map
structure, along with some functions that do things with the site map,
such as search and visit, etc. I also have some legacy code in my
application that requires the use of the site map data structure
directly, which is why I created an accessor method to get it. If I
don't use the CF function: duplicate () to return the instance data, I
get a horrible memory leak that brings down my server every day. The
jrun process basically comes to a screeching halt and CF returns blank
pages. I'm running CF8. I've found out that CF8 did have some memory
leaks in shared scopes.

Here's an article that pretty much sums up my experience:

http://thinkinglemur.com/index.php/2010/02/memory-leaks-with-coldfusion-8/

It is true that arrays are passed by value (which sucks!!!). But I'm
pretty sure that structures within the arrays themselves are passed by
value, unless you do a deep copy using the duplicate method.

Jared Rypka-Hauer

unread,
Feb 25, 2010, 12:47:05 PM2/25/10
to cfc...@googlegroups.com
Arrays are passed byValue, but structures within an array are passed by reference... meaning that if you return an array you're actually returning a copy of the array, but each array element containing a structure contains a pointer to the same struct as the original. Ultimately, what this means is that if you return an array from a function and modify it, the original is not changed; however if you return an array of structures and change one of the structs, the original IS modified.

Pseudocode isn't really going to be sufficient in this case as your problem is pretty unique.

Oh, and not var-scoping your function-local variables creates them in the variables scope... which, depending on the function, can create some really nasty race conditions and/or memory leaks.

Doing a deep copy with duplicate() does NOT RETURN ANYTHING BY REFERENCE... it's the only way to return a structure by value because it creates an entirely new structure that mirrors the original.

Let's see... anything else?

Oh, the fact that returning a struct from a CFC function in the application scope causes your app to return blanks pages pretty much tells me all by itself that the issue is your code and not CF. This is how all the major frameworks work, from Fusebox on up to ColdBox, so that's not the problem.

J

On Feb 25, 2010, at 11:39 AM 2/25/10, redtopia wrote:

> ... It is true that arrays are passed by value (which sucks!!!). But I'm


> pretty sure that structures within the arrays themselves are passed by
> value, unless you do a deep copy using the duplicate method.

> ...

redtopia

unread,
Feb 25, 2010, 1:19:59 PM2/25/10
to CFCDev
Yes, I know that storing CFCs in a shared scope wasn't the problem.
But I think my code exposed the memory leak bug in CF 8 that was fixed
in one of the CF 8 hot fixes, though I haven't tested that theory. I
solved the problem using the duplicate () function before I applied
the hot fixes, so I don't really know.

Let me ask you this:

You have a cfc that is stored in the application scope, and it stores
an array as part of its instance data. It also has a method that
changes the instance data (a refresh of the site map). And your
application also allows a developer to re-create that cfc because a
new version of that cfc might need to be applied to the application
(obviously a rare action).

Somewhere in you code, you need direct access to the instance data (in
this case to the site map data structure). Do you pass back a copy of
the instance data, or do you allow direct access to the instance data?

Here's what I do ( getSiteMap () returns a copy like this: <cfreturn
duplicate (this.siteMapArray) /> )

<cflock type="readonly" scope="Application" throwontimeout="true"
timeout="30">
<cfset siteMapArray = Application.siteMap.getSiteMap ()>
</cflock>

... then the code uses siteMapArray to do what it needs to do

So I lock the application scope (to handle a situation when a
developer would be re-creating the siteMap object), and then return a
copy of the instance data.

The code that handles when a developer would re-create the site map
is:

... in onRequestStart

<cfif (the developer wants to recreate the site map)>
<cflock type="exclusive" scope="Application" throwontimeout="true"
timeout="30">
<cfset Application.siteMap = createObject ("component",
siteMap).init ()>
</cflock>
</cfif>


What do you think?


On Feb 25, 10:47 am, Jared Rypka-Hauer <armchairde...@gmail.com>
wrote:

Jared Rypka-Hauer

unread,
Feb 25, 2010, 1:53:26 PM2/25/10
to cfc...@googlegroups.com
I think your locks may be the problem, you don't need to lock reads since J2EE handles that shit.

I also think that this will be the 3rd or 5th time someone said (or at least suggested) that pseudocode ain't gonna cut it here.

A couple inline comments:

J

On Feb 25, 2010, at 12:19 PM 2/25/10, redtopia wrote:

> Yes, I know that storing CFCs in a shared scope wasn't the problem.
> But I think my code exposed the memory leak bug in CF 8 that was fixed
> in one of the CF 8 hot fixes, though I haven't tested that theory. I
> solved the problem using the duplicate () function before I applied
> the hot fixes, so I don't really know.

If that were the case then your issues should have disappeared when you installed the hotfix. Since they didn't, I revert to my previous statement, the problem isn't CF, it's your code.

> Let me ask you this:
>
> You have a cfc that is stored in the application scope, and it stores
> an array as part of its instance data. It also has a method that
> changes the instance data (a refresh of the site map). And your
> application also allows a developer to re-create that cfc because a
> new version of that cfc might need to be applied to the application
> (obviously a rare action).

Are you hardcoding the site map into this CFC? Why? What purpose does any of this serve?

> Somewhere in you code, you need direct access to the instance data (in
> this case to the site map data structure). Do you pass back a copy of
> the instance data, or do you allow direct access to the instance data?

Neither. You code thread-safe functions on that CFC to deliver to the page whatever it needs, applying changes or receiving specific details.

> Here's what I do ( getSiteMap () returns a copy like this: <cfreturn
> duplicate (this.siteMapArray) /> )
>
> <cflock type="readonly" scope="Application" throwontimeout="true"
> timeout="30">
> <cfset siteMapArray = Application.siteMap.getSiteMap ()>
> </cflock>
>
> ... then the code uses siteMapArray to do what it needs to do

GET THE LOCKS OUT OF YOUR READS!! You do not need to lock reads to shared scopes, but you need to lock writes. Use NAMED LOCKS (again) instead of brute-force scope-wide locks.

> So I lock the application scope (to handle a situation when a
> developer would be re-creating the siteMap object), and then return a
> copy of the instance data.
>
> The code that handles when a developer would re-create the site map
> is:
>
> ... in onRequestStart
>
> <cfif (the developer wants to recreate the site map)>
> <cflock type="exclusive" scope="Application" throwontimeout="true"
> timeout="30">
> <cfset Application.siteMap = createObject ("component",
> siteMap).init ()>
> </cflock>
> </cfif>
>
>
> What do you think?

I think you have race conditions and thread-safety issues galore, and you're not taking what advice has already been given:

var-scope everything
stop locking reads from shared scopes
use named locks instead of scope locks

Read up on thread-safety and CFCs, too:

http://www.compoundtheory.com/?ID=21&action=displayPost (old, but still relevant)
http://corfield.org/blog/index.cfm/do/blog.entry/entry/CFCs_Scopes_amp_Thread_Safety (ditto)
http://www.cfhero.com/2009/11/being-thread-safe-in-coldboxcoldfusion.html (a bit simpler but much more recent)

redtopia

unread,
Feb 25, 2010, 2:45:31 PM2/25/10
to CFCDev
> If that were the case then your issues should have disappeared when you installed the hotfix. Since they didn't, I revert to my previous statement, the problem isn't CF, it's your code.

I solved the problem *before* I applied the hot fixes, so I'm not sure
if it was CF or my code, but since the problem never happened on my
development environment, even when load tested, (CF 9), and there's a
hot fix that fixes memory leaks in cfcs in shared scopes, I figured
it's a pretty good chance that it was the CF bug. That's not important
anyway... what's important is that I figure out what "should" work,
and then follow that method.

> Are you hardcoding the site map into this CFC? Why? What purpose does any of this serve?

In my application, web site content is stored in a hierarchical data
model inside a database. I construct a site map when the application
is initialized so I can use it to do various sitemap related things,
like building the site's menus, and displaying links to other pages
within a section, or displaying the user's current location within the
web site, etc... all the standard web site navigation stuff.

Why do you think I have race/thread safety issues galore? Does it hurt
to read-lock? If it's not necessary, I would assume that the
underlying CF implementation amounts to a noop.

I realize using named locks are better, and I use them a lot, but I
lock the application scope whenever I write to it. Isn't that what I'm
supposed to do?

And I think a named lock can be disadvantageous for session scope
data. In most cases, you wouldn't want to perform a named lock when
you're writing to the session scope because that would lock any other
request from other session from being executed for read-only access
into that lock. Is that a correct assertion? Alternatively, locking
the session scope, will only lock that specific user from read access,
and not everyone else. Correct?

Thanks for the links... I'll check them out. (FYI, I've been a CF
developer for 11 yrs. and I have a CS degree, so you're not helping
someone who is completely clueless!)


On Feb 25, 11:53 am, Jared Rypka-Hauer <armchairde...@gmail.com>
wrote:

> http://www.compoundtheory.com/?ID=21&action=displayPost(old, but still relevant)http://corfield.org/blog/index.cfm/do/blog.entry/entry/CFCs_Scopes_am...(ditto)http://www.cfhero.com/2009/11/being-thread-safe-in-coldboxcoldfusion....(a bit simpler but much more recent)

Reply all
Reply to author
Forward
0 new messages