If you are working on a site that gets a decent amount of traffic and are wondering how to speed up performance there are a number of things to try. One of the most beneficial is component caching (persisting CFCs).

When using the cfinvoke tag or the CreateObject function on your pages, it adds CPU time to process the requests to load all of those objects into memory. Under load this can make quite a difference in your page response times. One way to get around it is to put those objects into a shared scope so they only get instantiated once. This will save memory and CPU time on your server. Let’s get into it.

Application.cfm
<cfapplication name="yourApp" sessionmanagement="yes" sessiontimeout="#CreateTimeSpan(0,0,30,0)#">
<!--- Determine if we need to load the component manager cfc --->
<cfif not StructKeyExists(application,'componentManager') OR isDefined("URL.init")>
    <cfset loadCom = true>
</cfif>
<cfif loadCom>
    <!--- Use a named lock for shared scopes, does not lock the entire scope, just this block of code --->
    
<cflock name="loadComponentManager" timeout="10" type="exclusive">
    
    <!--- Create the componentManager object and run the init() function --->
        
<cfset application.componentManager = CreateObject("component","path.to.componentManager").init()>
    </cflock>
</cfif>

<!--- Most of my UDF’s are already available in the request scope, you can create a pointer to application.componentManager if easier --->
<cfset request.componentManager = application.componentManager>

/path/to/componentManager.cfc
<cfcomponent displayname="Component Manager" hint="Handles caching and distribution of components.">
    <!--- The init function creates the structure that will hold instances of our components --->
    <cffunction name="init">
        <cfset cachedComponents = structNew()>
        <cfreturn this>
    </cffunction>

    <cffunction name=
"getComponent">
        <cfargument name=
"component" required="yes" type="string">
        <cfset var path =
"path.to.#arguments.component#">
        <!--- Check to see if the component requested already exists in the structure, if it does not – create it and store it --->
        <cfif not structKeyExists( cachedComponents, path )>
            <cflock name=
"loadComponent" timeout="10" type="exclusive">
            <cfif not structKeyExists( cachedComponents, path )>
                <cfset tComponent = CreateObject(
"component",path)>
                <!--- The structure key for this component is going to be the full path to it, while the structure value will be the component itself --->


                <cfset tempVar = StructInsert(cachedComponents,path,tComponent,1)>
            </cfif>
            </cflock>
        </cfif>

        <!--- Return the cached copy of the component back to the user --->

        <cfreturn cachedComponents[path]>
    </cffunction>

</cfcomponent>

Referencing the cached components
You can reference your cached components like so:

<cfscript>
    // The getComponent function takes only 1 argument, the name of the CFC you are trying // to call.

    // separated
    myObj = request.componentManager.getComponent(‘testCFC’);
    myQuery = myObj.getMyQuery();

    // All in 1 line
    
myQuery = request.componentManager.getComponent(‘testCFC’).getMyQuery();
</cfscript>

IMPORTANT NOTES
There are a few very important things to remember when using this technique. Once implemented you will have changed the nature of your CFC’s, no longer do you create a whole new instance for each user, all users now share the same instance of the component. The componentManager.cfc is responsible for dishing out the cached versions. This means no instance data in any cached CFC’s. Anything written to the ‘variables’ scope (or ‘this’ scope) inside your CFCs effectively becomes the same as an application variable by nature (and would need to be locked to be used properly). This means to avoid race conditions, do not keep instance data for any cached cfc. In fact, I find it best to var ALL local variables in my cached functions like so:

<cffunction name="someTestFunction" access="public" returntype="query" displayname="My Name" hint="Describe Me">
    <cfargument name=
"userID" required="yes">
    <!--- setting these variables first is important so you don’t get any other users’ data when you run the function, notice I even var the query names --->
    <cfset var qGetUsers = ‘’>
    <cfset var tDate =
‘’>

    <cfset tDate = now()>

    <cfquery datasource=
"queryDSN" name="qGetUsers">
        SELECT * FROM USERS WHERE hireDate < #tDate# and userID = #arguments.userID#
    </cfquery>

    <cfreturn qGetUsers>

</cffunction>

Sometimes it can be a bit tricky to diagnose when you actually do have concurrency issues (race conditions), so it’s best to follow a few coding guidelines to make sure your functions are safe for persisting.

About This Tutorial
Author: Derrick Anderson
Skill Level: Advanced 
 
 
 
Platforms Tested: CFMX,CFMX7
Total Views: 146,007
Submission Date: August 31, 2006
Last Update Date: June 05, 2009
All Tutorials By This Autor: 2
Discuss This Tutorial
  • 1. as long as you are in fact 'scoping' the variable i don't see a reason why calling the request scope would be a problem. 2. you can do exactly as you mentioned to call methods in other cfc's.

  • i find the above tutorial be of great use, Thanks for sharing.One thing i want make myself or for others who are in the same boat, 1-is it advisable to access request scoped variables from within the cfc, is there any performace issue. 2-how can i call another cfc's function (for example user.cfc needs to call manager.cfc to execute some function).Since i placed the user.cfc and manager.cfc in application scope.is it advisable to call manager.cfc from within user.cfc like and i can use the variable "somevariable" in user.cfc pladvice. Regards Riyaz

  • the load test results are always going to be dependant on how cfc intensive your app is. If you use cfc's for everything, many calls to cfc's on each page request then obviously your results will be much better than if you only have a few cfc's that do not get called much. I can put up load test metrics that i have from using this approach but you're not likely to have the same results as I did so i'm afraid it would be misleading. You can tell if you will benefit from doing this fairly easy, just load up openSTA, JMeter, Grinder, (or your favorite load testing software), add the code above and change a group of cfc calls to call cached cfc's and watch the results. (load test it before making changes obviously so you can compare)

  • do you have any metrics that show how much savings this approach saves on the app server?

Advertisement

Sponsored By...
$39.00 - 50 Minute Deep Tissue Massage Dripping Springs, Texas!