Orange is my favorite color

Flowers?  Jerry, you're so sweet...

I found out not long ago that my good friend and college housemate Jerry Tam had made the cut and would be appearing on Season 5 of Bravo TV’s Project Runway. I’m currently in Europe right now so I went looking to find some details about the season premiere last night. Knowing Jerry as well as I do, he was either going to win it all or get kicked off the first episode and unfortunately it was the latter.

Tam has an incredible amount of talent and vision wound up into a 150 beats-per-minute kind of frenetic energy. This is a guy who went from growing up in Montana bussing tables in his dad’s Chinese Restaurant to start his own fashion label in Manhattan, FORM. That was not the shortest path between two points.

People think succeeding in business just requires a great idea but ideas don’t blossom on their own. Success takes hard work, long nights, persistence and an undying conviction that you can do it better than everyone else. While he may have lost out on Project Runway, I know Jerry has those qualities required to succeed.

You’re still 1 of 16 buddy and we’re all proud of you here on the left coast!

I’m testing an application on ColdFusion 8 in preparation for an upgrade and have found a strange error. My reinit process clears the trusted cache using the Admin API along with restarting Coldspring and Model-Glue. On CF7, the following code works very reliably:

<cfinvoke component="cfide.adminapi.administrator" method="login">
	<cfinvokeargument name="adminPassword" value="..." />
</cfinvoke>

<cfinvoke component="cfide.adminapi.runtime" method="clearTrustedCache" />

Since updating to CF8 on my Windows XP laptop, running this code as either part of my reinit process or in a standalone template results in the following coldfusion.security.SecurityManager UnauthenticatedCredentialsException error:

The error occurred in runtime.cfc: line 656
-1 : Unable to display error's location in a CFML template.

Stack Trace
at
cfruntime2ecfc1355728568$funcCLEARTRUSTEDCACHE.runFunction(E:\cf8_updates\cfusion\wwwroot\CFIDE\adminapi\runtime.cfc:656)
at
cfApplication2ecfc221588290$funcONREQUESTSTART.runFunction(Application.cfc:85)

coldfusion.security.SecurityManager$UnauthenticatedCredentialsException
	at
coldfusion.security.SecurityManager.authenticateAdmin(SecurityManager.java:1704)
	at
coldfusion.runtime.RuntimeServiceImpl.clearTrustedCache(RuntimeServiceImpl.java:1518)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at coldfusion.runtime.StructBean.invoke(StructBean.java:511)
	at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2300)
	at
cfruntime2ecfc1355728568$funcCLEARTRUSTEDCACHE.runFunction(E:\cf8_updates\cfusion\wwwroot\CFIDE\adminapi\runtime.cfc:656)
	at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:418)

Many people on the CFGURU list including Charlie Arehart and Cameron Childress made several suggestions of things to check but everything came back looking correct. I was prepared to chalk this up to a random issue with my installation until I asked others to try it on their CF8 install and was surprised by the results. While several people were unable to reproduce the error, we had confirmations of the same CF8 error on the following platforms:

  • Enterprise 8.0.1 multi-server on Windows XP
  • Standard 8.0.1 on Windows 2003
  • Enterprise 8.0.1 multi-server on Mac OSX

There are some people on 8.0.1 who have it working successfully but with three different people experiencing the issue it doesn’t seem to be a one-off installation error. If I find a resolution I will post it here as a follow-up but at this point I am going to comment out that line in my reinit since on my laptop I don’t use the trusted cache in development.

It’s pretty common knowledge that the Java 6 JRE in ColdFusion 8 has a bug from Sun that makes loading CFC-heavy applications slow. This impacts most framework-based apps that use Model-Glue, Mach-II, Coldspring, Transfer and so forth. To be clear, it’s not the frameworks, it’s a bug in the underlying Sun JRE.

I have some timing in my Application.cfc and index.cfm in my model-glue app that produced some interesting results different from my expectations. The difference is between reinitializing an application on a running CF8 server and starting up the CF8 server from scratch. I track the time it takes to load beans into Coldspring and for Model-glue to initialize with the following code in application.cfc:

<cfset cnt = getTickCount() />
<cfset application.cs.loadBeansFromXmlFile(expandPath("coldspring.xml"), true) />
<cflog file="application" type="information" text="Finished ColdSpring load in #(getTickCount()-cnt)/1000# seconds" />

And in index.cfm:

<cfif NOT structKeyExists(application, ModelGlue_APP_KEY)>
	<cfset booInit = true />
	<cfset cnt = getTickCount() />
<cfelse>
	<cfset booInit = false />
</cfif>

<cfinclude template="/ModelGlue/unity/ModelGlue.cfm" />
<cfif booInit>
	<cflog type="information" file="application" text="ModelGlue #ModelGlue_APP_KEY# initialized in #(getTickCount()-cnt)/1000# seconds" />
</cfif>

Now my expectation is that it would be slower on a cold start to load these applications than restart but the reverse is what I see by a factor of 2-1. My reinit routine performs the following:

  • Clears session key
  • Clears trusted cache via Admin API
  • Clears Transfer cache
  • Deletes Coldspring instance
  • Recreates Coldspring instance (this is timed)
  • Clears all Model-glue Application keys to force reload
  • Reinitialized session
  • Runs index.cfm to initialize Model-Glue (this is timed)

The times below are from starting CF8, loading my application home page, then immediately performing a warm reinit:

Cold Start   Warm Reinit   Ratio
Coldspring   20.7s 48.2s 2.3x
Model-Glue   29.3s 55.8s 1.9x

Anyone know why this would be the case? I would think the classes had already been loaded so a warm reinit should be faster than the cold start.

If this is true in general, then it’s better to restart the CF8 server when deploying code than running a reinit procedure.

In the same member merging process described in my last post, I was running a bit of code with a TRY/CATCH that looked for a duplicate primary key condition and, if found, deleted it from the source account:

<cfquery name="select" datasource="#variables.datasource.getName()#">
	SELECT *
	FROM roles
	WHERE id = '#id#'
</cfquery>

<cfloop query="select">

	<cftry>
		<cfquery name="update" datasource="#variables.datasource.getName()#">
			UPDATE roles
			SET id = '#target#'
			WHERE id = '#id#'
			AND role = '#role#'
		</cfquery>

		<cfcatch type="database">
			<cfquery name="delete" datasource="#variables.datasource.getName()#">
				DELETE FROM roles
				WHERE id = '#id#'
				AND role = '#role#'
			</cfquery>
		</cfcatch>
	</cftry>

</cfloop>

The problem is that when the UPDATE failed due to a duplicate key, the CFTRANSACTION aborted the rest of the routine. I received this error in the console:

Error Executing Database Query.
ERROR: current transaction is aborted, commands ignored until end of transaction block

SAVEPOINT Solution

Via Google, I learned about PostgreSQL SAVEPOINTs, which allow you to back up your transaction to some intermediate point and continue as if it had not happened. They were easy to implement:

<cfquery name="select" datasource="#variables.datasource.getName()#">
	SELECT *
	FROM roles
	WHERE id = '#id#'
</cfquery>

<cfloop query="select">

	<cftry>
		<cfquery name="update" datasource="#variables.datasource.getName()#">
			-- declare my savepoint
			SAVEPOINT troles;

			UPDATE roles
			SET id = '#target#'
			WHERE id = '#id#'
			AND role = '#role#';
		</cfquery>

		<cfcatch type="database">
			<cfquery name="delete" datasource="#variables.datasource.getName()#">
				-- a collision occurred, skip back to the savepoint and then delete
				ROLLBACK TO SAVEPOINT troles;

				DELETE FROM roles
				WHERE id = '#id#'
				AND role = '#role#';
			</cfquery>
		</cfcatch>
	</cftry>

</cfloop>

Note it requires a semi-colon to run both statements in a single CFQUERY. This says that if the CFCATCH runs, to skip back to where we were just before our UPDATE caused an index error (duplicate keys) and instead run the delete to eliminate the duplicate data (which in my business process here, is OK). Now collisions could be handled without breaking the top level transaction.

There are other ways to do this like use a SELECT query first to see if the collision is going to happen. In this process however, collisions are very rare but datasets can be large so for performance I decided it was “easier to ask for forgiveness than permission”. :)

I don’t know if this is required in other databases besides PostgreSQL but I believe save points are available with most vendors. My member merge process runs about 70 queries to join together two accounts and all of their data before it declares the operation complete. This a neat feature to help control that lengthy process.