Performance considerations should always be on the mind of any quality developer. ColdFusion application performance often is rooted in the application code itself. In instances where code is written improperly or when necessary code elements are not included at all, performance and functionality can suffer dramatically. The following sections discuss issues that are key to improving performance in your applications.
Locking of Server, Application, and Session Variables
A commonly missed item dealing with application performance is the locking of shared scope variables. These variables should be locked in any code where server, application, or session-scoped variables are either written to or read from. Appropriate locking of server, application, and session variables must be applied or you risk degrading application performance and crashing your server.
Unless you properly implement locking, memory corruption can and will occur, which would further cause the ColdFusion Server to have threads hang when simultaneous user load is applied. To stop the inevitable memory leak, you must use the CFLOCK statement to prevent memory collision.
For writing to a scoped variable, use the type="exclusive" attribute:
<cflock scope="Application" type="exclusive" timeout="10"> <cfset Application.myAppVar = 1> other Application variable statements … </cflock>
For reading a scoped variable, use the type="readonly" attribute:
<cflock scope="Application" type="readonly" timeout="10"> <cfset anyVariable = Application.myAppVar > other Application variable statements … </cflock>
You can group multiple variables if they are all reads or all writes:
<cflock scope="Application" type="exclusive" timeout="10"> <cfset Application.myAppVarX = 1> <cfset Application.myAppVarY = 2> <cfset Application.myAppVarZ = 3> other Application variable statements … </cflock>
Also, never mix scoped variable types within a CFLOCK tag. Thus, the following would not be appropriate:
<cflock scope="Application" type="exclusive" timeout="10"> <cfset Session.mySessionVar = 1> <cfset Application.myAppVar = 1> </cflock>
The following also would not be appropriate:
<cflock scope="Application" type="exclusive" timeout="10"> <cfset anyVariable = Application.myAppVar> <cfset Application.myAppVar = 1> </cflock>
For more information on how to properly lock access to memory variables, refer to Chapter 8, "Application Framework."
Assigning Values to Application Variables for Each Page Request
When using the Application.cfm file (which is loaded with each page request) to assign application-scoped variables, be certain to encapsulate the variable assignments within a CFIF statement. Without doing this, the variables would be assigned for every page in the site, and thus would not be used properly. Simply test if an application-scoped variable exists. If it doesn't exist, assign all application-scoped variables. If it does exist, the assignment code is bypassed.
When setting application variables in the Application.cfm file, wrap the statement(s) within a CFIF statement:
<cflock type="Application" type="exclusive" timeout="10"> <cfif NOT IsDefined("Application.ApplicationName"> <cfset application.applicationvar1="some value"> <cfset application.applicationvar2="another value"> </cfif> </cflock>
It is also important to note that this same technique should be used if session scoped variables are being assigned within the Application.cfm template.
Overuse of CFOUTPUT
ColdFusion developers have heard for years that ColdFusion parses any code that lies within a CFOUTPUT tag. This has all too often led developers to write code which grossly overuses the CFOUTPUT tag and causes ColdFusion Server to work harder.
Developers should make it a goal to write clean and readable code, of course, and in regards to the CFOUTPUT tag, developers should have only as many of them on a single page as absolutely necessary. The following example shows overuse of the CFOUTPUT tag:
<input type="text" value="<cfoutput>#myValue1#</cfoutput>"... <input type="text" value="<cfoutput>#myValue2#</cfoutput>"... <input type="text" value="<cfoutput>#myValue3#</cfoutput>"...
Instead of the preceding code, you should wrap a single CFOUTPUT tag around a block of code that contains several variables to be evaluated:
<cfoutput> <input type="text" value="#myValue1#"> <input type="text" value="#myValue2#"> <input type="text" value="#myValue3#"> </cfoutput>
This code executes faster than the previous example and is easier to read and maintain.
Avoid Using Subqueries
It is a good practice to avoid using subqueries in your SELECT statements. The reason is that this causes the database engine to create an execution plan that is not or might not be the most efficient. Because the database engine does not know what the final values of the subquery might be, it must plan to look through the first table or set of tables as many times as the number of records that could be returned by the subquery.
This is an example of using a subselect:
<cfquery name="getEmployees" datasource="ExampleApps"> SELECT FirstName , LastName , Email FROM tblEmployees WHERE DeptIDFK IN (SELECT DepartmentID FROM tblDepartments WHERE DepartmentName = 'Web Development') </cfquery>
This is an example of using multiple queries:
<cfquery name="getDepartment" datasource="ExampleApps"> SELECT DepartmentID FROM tblDepartments WHERE DepartmentName = 'Web Development' </cfquery> <cfquery name="getEmployees2" datasource="ExampleApps"> SELECT FirstName , LastName , Email FROM tblEmployees WHERE DeptIDFK IN (#ListQualify(ValueList(getDepartment.DepartmentID), "'")#) </cfquery>
Use CFPARAM to Set Default Values
Developers often use IF statements within their applications to test for the existence of a variable and to set default values if that variable does not exist. This can be streamlined by using the CFPARAM tag to assign default values to a variable. The following example illustrates this point:
<cfif NOT IsDefined("myVarName")> <cfset myVarname=1> </cfif>
The same logic can be replicated as follows:
<cfparam name="myVarName" default=1>
Use CFSWITCH Instead of Multiple CFIF Statements
Developers should never use multiple IF statements to test for different values of the same variable. If there are more than two possible values for a single variable, you should use the CFSWITCH/CFCASE statement to enhance performance:
<cfif lastname = "Ross"> <cfset myOtherVar="foo"> </cfif> <cfif lastname = "Cummings"> <cfset myOtherVar="bar"> </cfif> <cfif lastname = "Sen"> <cfset myOtherVar="foobar"> </cfif>
This logic works, but is not very efficient. If you use this type of code, each and every CFIF statement is evaluated for every pass through the page unnecessarily. In other words, if the first CFIF statement is true, the other CFIF statements really don't need to be evaluated the condition has been met. Even though the first condition might be true, all the following CFIF statements are evaluated unnecessarily. To enhance performance, change this logic to use the CFSWITCH statement instead:
<cfswitch expression="#lastname#"> <cfcase value="Ross"> <cfset myOtherVar="foo"> </cfcase> <cfcase value="Cummings"> <cfset myOtherVar="bar"> </cfcase> <cfcase value="Sen"> <cfset myOtherVar="foobar"> </cfcase> </cfswitch>
With this logic, only the statement that evaluates to true is executed, and the other conditions are bypassed. This takes the load off the processor by not requiring it to evaluate multiple CFIF statements.
Two Function Calls to a Database to Retrieve the Unique ID of Inserted Records
Many developers commonly use two query calls to retrieve the value of a newly created record. The first query executes the insert of the new row into the table. The second query uses the MAX() function of the structured query language (SQL) to retrieve the record with the highest unique ID value.
<cfquery name="InsertNames" datasource="#request.dsn#"> INSERT INTO Authors (firstname, lastname, email) VALUES ('#form.fname#', '#form.lname#', '#form.email#') </cfquery>
This request does not return any values so it requires a second query. The next request retrieves the MAX identity field, which might or might not come from the previous INSERT query.
<cfquery name="getNewRecord" datasource="#request.dsn#"> SELECT FROM Authors MAX(AuthorID) </cfquery>
Although this method does work under a minimal load, it is possible that errors can occur when the site is put under heavier load. If multiple users attempt to run the page at the same time, there is a chance that one of the values might become orphaned, which can result in database errors within the application.
It is better to retrieve the unique ID in a single query. By doing this, you eliminate the loss of data and improve application performance. You also alleviate the need to make multiple requests to the database.
Although this approach does work, the same functionality can be achieved with the following:
<cfquery name="InsertNames" datasource="#request.dsn#"> INSERT INTO Authors (firstname, lastname, email) VALUES ('#form.fname#', '#form.lname#', '#form.email#') SELECT AuthorID </cfquery>
Now the single request to the database inserts the record and returns the value of the identity field of the newly created record. This method ensures that no records are orphaned no matter how many users execute the page simultaneously. In addition, you get a performance enhancement by making only one request to the database.
Do Not Overuse the # Symbol
The # symbol is for outputting the values of ColdFusion variables to the browser. It is not needed within a ColdFusion tag such as CFSET or CFIF. You need to wrap the variable name with the # symbols only when the variable is used within an area where quotation marks are used. This concept is best explained within the following code snippet:
<cfif UserID EQ 33> <!--- ColdFusion Statements ---> </cfif>
Now assume you need to use the variable as part of another variable assignment:
<cfif UserID EQ 33> <cfset formAction = "updateRecord.cfm?uID=#userID#"> </cfif>
This time, the # symbols had to be in place to correctly assign the value of the formAction variable because the variable (UserID) is inside the quotation marks. Had the variable been outside the quotation marks, the # symbols would not have been used:
<cfif UserID EQ 33> <cfset formAction = "updateRecord.cfm?udocText">Both of the two previous examples give the variable formAction a value of "updateRecord.cfm?uID=33". If the # symbol was not used and the variable was inside the quotation marks, as in the following code:<cfif userID = 33> <cfset formAction = "updateRecord.cfm?uID=userID"> </cfif>