Notes from the field on ColdFusion (or related) technical issues.

Saturday, June 25, 2005

Oracle Connection Lock Timeout and ColdFusion 5

If you're getting Connection Lock Timeouts or just generally poor performance with ColdFusion 5 using Oracle OCI drivers, here's why:

When ColdFusion 5 was being released, there was a bug in the Oracle OCI driver set that made simultaneous execution of multiple statements quite unreliable. (However, retrieving multiple resultsets could be done concurrently; this only applies to the time between when a statement is submitted for execution and when the first result row is available.) In the interest of stability, ColdFusion was made to single-thread it's access to that part of the driver; however, an undocumented switch was left behind for when that Oracle bug was fixed.

If you're running the OCI driver set, version 8.1.7 or newer, then you can remove this restriction.

The undocumented registry key that can be added to probably eliminate the "Oracle Connection Lock Timeout" and "Oracle Statement Lock Timeout" errors is:

HKEY_LOCAL_MACHINE\SOFTWARE\Allaire\ColdFusion\CurrentVersion\Server\ UseApplicationLockingForOracle8

Create this key and set it to a STRING value of "0" (zero) to remove statement locking. This feature is only available with ColdFusion 5, and the lock only applies versions of ColdFusion prior to ColdFusion MX. On Unix versions of ColdFusion 5, you need to stop ColdFusion, then carefully edit the cf.registry file to add this key to the correct place. (Or just use CFREGISTRY to add the key, then restart cfserver.)

Wednesday, June 22, 2005

JRun Clustering with Windows 2003 NLB

There are a couple of tricky points to clustering ColdFusion or JRun with Windows Network Load Balancing enabled.

First, see Brandon Purcell's article for general information on JRun clustering with ColdFusion 6.1:
http://www.bpurcell.org/viewContent.cfm?ContentID=121

For ColdFusion 7 in Multi-Instance mode, use the ColdFusion Administrator to create the instances to cluster. Remember, each instance in a cluster has to have a unique name; I like to use the last part of the IP address in the name, e.g. "cfusion66". Once all instances are created, choose one ColdFusion 7 server to be your "cluster manager", and use the Administrator on that machine to create the cluster by registering the remote instances running on the other machines, then creating the actual cluster. Once that's done, you must restart all instances involved in the cluster, then use the WSCONFIG utility to re-install the Web server connector on every Web server involved. In order to access the "cfusion" instance to manage instances and clusters after that, you must go to the jrun4/servers/cfusion/cfusion-ear/cfusion-war/WEB-INF/jrun-web.xml and add an entry to point to your CFIDE folder, which may look like this:

<virtual-mapping>
<resource-path>/CFIDE</resource-path>
<system-path>C:/inetpub/wwwroot/CFIDE</system-path>
</virtual-mapping>

Then, you can access the cluster-manager ColdFusion 7 Administrator by pointing your browser to port 8300 on the server you want to manage.

I found the easiest / most reliable way to get this working is to use one interface in Multicast mode, and if necessary, add a static ARP entry to the router:
"Some routers require a static ARP entry because they do not support the resolution of unicast IP addresses to multicast media access control addresses. For example, Cisco routers require an ARP (address resolution protocol) entry for every virtual IP address. While Network Load Balancing uses Level 2 Multicast for the delivery of packets, Cisco's interpretation of the RFCs is that Multicast is for IP Multicast. So, when the router doesn't see a Multicast IP address, it does not automatically create an ARP entry, and one has to manually have to add it on the router."

(Source: http://www.microsoft.com/technet/prodtechnol/windowsserver2003/technologies/clustering/nlbbp.mspx)

If you DO have two interfaces, make sure they're both attached to the same LAN segment, and that the NLB interfaces are the secondary interfaces.

If they're not on the same LAN segment, then the cluster will usually fail to synchronize with the other members, and you'll have severe problems getting session failover working reliably.

If the NLB interface is left as the primary interface, then connections to the JNDI port fail (the TCP connections are closed as right after they are opened, with no actual data transferred), and the instance manager shows "network error" for remote instances.

With multiple interfaces active, it may also be necessary to add one or more UnicastPeer attribute(s) in the ClusterManager section of jrun4\servers\{instance}\SERVER-INF\jrun.xml

That being said, I found that disabling the secondary NIC and using Multicast mode worked well, each and every time, without having to do anything beyond registering the remote instance and adding it to the cluster.

Thanks to Matt Stevanus for finding the network order dependency.

Monday, June 20, 2005

"Daryl" neq "Macromedia"

Last Friday, 17 June 2005, was the final day of employment for me at Macromedia. On that day, I received a Fedex box containing my final paycheck, and my 5-year "thanks for staying" watch.

Seems someone at Macromedia is not without a sense of irony.

I'm now working for Webapper Services, LLC, the makers of SeeFusion, an excellent tool for monitoring what's going on inside your ColdFusion server at any given moment. Initially, I'm going to be doing onsite consulting, but the plan is that I'm going to get more and more involved in the SeeFusion product line, which I very much look forward to.

Friday, April 01, 2005

Macromedia Announces Plan to Make ColdFusion Harder

In an effort to compete 'on a more level playing field', Macromedia announced today that it would be making ColdFusion substantially more difficult to use.

This unusual move was prompted, said Damon Jordahl, a product manager at Macromedia, by ColdFusion's continuing bad reputation as a 'toy' language. 'We've been getting a bad reputation for years,' he said, 'because people would look at ColdFusion, produce a working database driven report page in a couple of minutes, and discard it as being way to easy and fast to be considered as a "serious" language.'

Larry Wall, founder of the Perl language, applauded the move. 'For the longest time, Perl has been alone as the world's only self-encrypting language. I'm hoping that Macromedia will give me some real competition in this space.'

The success of ASP.NET was also a factor in the decision, when interviews with ASP developers revealed that one of the main reasons they use the language is its inefficiency. One anonymous ASP developer was cited as saying, 'Well, it takes weeks to get anything useful done in ASP. With ColdFusion, I can get entire working sites done in hours or maybe a few days. Where's the job security?'

Complexity also provides a convenient barrier to entry. 'Let's face it', concluded Jordahl, 'we've been attracting the novice developer for years. People who had never before written a line of machine instruction in their life. And novice programmers just don't have the experience to make scalable sites. Half the time they don't even index their database tables, and that's just a recipe for scalability disaster. But the programmers and the DBAs don't get the bad reputation. People always want to blame ColdFusion. We think that adding substantial complexity to the language will exclude these entry level people from the game, and allow ColdFusion to get the good reputation it deserves.'

Macromedia plans to ship the newly obfuscated ColdFusion language in its next release version.

(Happy April Fool's Day!)

Wednesday, March 30, 2005

CFDirectory can be Slow with Many Files

CFDirectory can be rather slow when working with directories containing very many files (as in, thousands or tens of thousands of files.)

The underlying method used, java.io.File.listFiles(), does not allow a Java application to pass a filter to the underlying operating system, but rather forces the application to filter the directory listing on a case-by-case basis by creating a FileFilter object.

If the thousands of file names exist across a network share, the problem is compounded by the added latency of the network file system.

If you're only looking for, say, a date stamp for a single file, the entire directory is iterated looking for the file, and only then are the details of the file made available to the ColdFusion application.

If you only want information about a single file, it's fairly easy to get that directly via direct calls to Java from within ColdFusion. Here is a ColdFusion function that will efficiently get the date stamp from a single file:

<cfscript>
function getFileDate(fn) {
    theFile = createObject("java","java.io.File");
    theFile.init(fn);
    if( theFile.exists() ){
        theDate=createObject("java","java.util.Date");
        theDate.init(theFile.lastModified());
        dateString = 1+theDate.getMonth() & "/" & theDate.getDate() & "/" & 1900+theDate.getYear() & " " & theDate.getHours() & ":" & theDate.getMinutes() & ":" & theDate.getSeconds();
    }
    else {
        dateString="";
    }
    return dateString;
}
function getFileSize(fn) {
    theFile = createObject("java","java.io.File");
    theFile.init(fn);
    if( theFile.exists() ){
        return theFile.length();
    else {
        dateString="";
    }
    return dateString;
}
</cfscript>

TCP/IP Efficiency

Ran into an interesting problem the other day.

Seems that the TCP connection between JRun (which underpins ColdFusion) and its Web server connector can get somewhat overwhelmed by the bursty nature of ColdFusion pages.

(Normally, ColdFusion doesn't send any data back to the Web server until the page is completely finished, as the last line of a template could be a CFLOCATION or CFHEADER tag, and once the HTTP header is sent, it can't be altered nor retracted.)

Using Ethereal to analyze the communication, it became clear that the JRPP connections (JRPP is the protocol used for JRun's Web server connectors to talk to JRun itself) were stalling because of something called "Silly Window Syndrome" avoidance (see RFC 1122), when the receive TCP buffer at the Web server was getting quickly filled. The stalling was prevented by increasing the TCP max window size in Windows by making a few registry settings.

Here's the contents of the registry file that adjusts the TCP max window size to roughly 128k (instead of the default ~17k):
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"GlobalMaxTcpWindowSize"=dword:00020148
"TcpWindowSize"=dword:00020148
"Tcp1323Opts"=dword:00000003
This also helped speed up my Internet downloads for my home PC. I'd recommend applying the change to any Windows box with more than 256MB RAM.

For more on TCP options under Windows, see
this TechNet article.

Avoid Evaluate()

The ColdFusion Evaluate() function is often used to resolve dynamically named variables, such as form variables.

The evaluate function, however, forces ColdFusion to compile the expression being evaluated every time you call it, which can be a strain on performance, especially since parts of the compilation process are necessarily single-threaded.

Almost all instances of Evaluate() can be avoided by using structure syntax; instead of writing:

<cfset value=evaluate("form.field#i#")>

You can write:

<cfset value=form["field#i#"]>

Which is much faster, and better yet, simpler.

This doesn't work quite so well when looping over a query, however. If you use structure syntax to access a query column, you must also specify the row number. So, where this would work:

<cfoutput query="q">
#evaluate("q.#fieldname#")#<br>
</cfoutput>

You'd have to use q.currentrow to get the correct row when avoiding evaluate():
<cfoutput query="q">
#q[fieldname][q.currentrow]#<br>
</cfoutput>

Update: 7Apr2005

Classes created for evaluate() [and de() and setVariable()] are cached on a literal string basis (sorta like cached queries work for the exact string of the query), so the performance impact isn't quite so bad, so long as the literal string for evaluate() doesn't change every time.