Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Jan. 2009) with:

Dealing With RequestTimeOut And The OnError Application Event Method (Help Me)

By Ben Nadel on
Tags: ColdFusion

I was reading over on Dan Vega's blog this morning about request time outs when I came up with a question: How do you deal with handling time outs when your OnError() event method takes a bit of time to run? Take this simple ColdFusion example as demonstration.

First, I set up a very simple Application.cfc ColdFusion component:

  • <cfcomponent>
  •  
  • <!--- Set up global page processing. --->
  • <cfsetting
  • requesttimeout="5"
  • showdebugoutput="false"
  • />
  •  
  •  
  • <cffunction
  • name="OnError"
  • access="public"
  • returntype="void"
  • output="true">
  •  
  • <p>
  • You have errored out.
  • </p>
  •  
  • <p>
  • Error: #ARGUMENTS[ 1 ].Message#
  • </p>
  •  
  • </cffunction>
  •  
  • </cfcomponent>

This Application.cfc merely sets up the request time out for page requests and defines an OnError() event method. Then, I set up a simple page that was designed to run longer than the request time out:

  • <!---
  • Set up a bunch of CFHttp calls to force the page to
  • run longer than the set request time out.
  • --->
  • <cfhttp url="http://www.google.com?q=#Rand()#" />
  • <cfhttp url="http://www.yahoo.com?q=#Rand()#" />
  • <cfhttp url="http://www.cnn.com?q=#Rand()#" />
  • <cfhttp url="http://www.searchgalleries.com?q=#Rand()#" />
  • <cfhttp url="http://www.espn.com?q=#Rand()#" />
  • <cfhttp url="http://www.houseoffusion.com?q=#Rand()#" />
  • <cfhttp url="http://www.fullasagoog.com?q=#Rand()#" />
  • <cfhttp url="http://www.ask.com?q=#Rand()#" />
  • <cfhttp url="http://www.cnet.com?q=#Rand()#" />
  •  
  • <!--- Success! --->
  • Done grabbing content.

Since I have to get and grab the content from each of the URLs above, this should take more than the allowed five second time out. And, indeed, running that page does throw a time-out error:

You have errored out.

Error: The request has exceeded the allowable time limit Tag: cfhttp

So far, the OnError() event method works fine. But what happens when the OnError() event is not quite so simple. Let's update the event method so that it gives us more information:

  • <cffunction
  • name="OnError"
  • access="public"
  • returntype="void"
  • output="true">
  •  
  • <p>
  • You have errored out.
  • </p>
  •  
  • <p>
  • Error: #ARGUMENTS[ 1 ].Message#
  • </p>
  •  
  • <!--- Dump out the error arguments. --->
  • <cfdump
  • var="#ARGUMENTS[ 1 ]#"
  • top="1"
  • />
  •  
  • </cffunction>

Now, in addition to dumping out the error message, we are going to dump out the top level values of all argument variables. If we run the page again, we now get this error:

You have errored out.

Error: The request has exceeded the allowable time limit Tag: cfhttp

The web site you are accessing has experienced an unexpected error. Please contact the website administrator.

The request has exceeded the allowable time limit Tag: cfoutput

The error occurred in D:\...\Application.cfc: line 26

24 :
25 : <!--- Dump out the arguments. --->
26 : <cfdump var="#ARGUMENTS[ 1 ]#" top="1" />
27 :
28 : </cffunction>

As you can see, the OnError() event method is still catching the CFHttp time out, but by that time, the OnError() event method has no "request time out" left for itself to process anything.

I run into this problem a lot with CFQuery and CFExecute. Does anyway have a way to handle this more elegantly? Perhaps OnError() is not the right way to handle request time outs?




Reader Comments

Ben,
Here are some additional thoughts. In your example you set the request timeout to 5 seconds. I understand that was for purposes of the example but lets also examine a fairly normal operation. I can see a page timeout because of the fact that it has to output so much data to a screen but what scenario is a query going to timeout like this? The default setting for a request timeout is 60 seconds. If you have queries that are taking in excess of 60 seconds on a daily basis then I think the sql needs to be examined. I know this article is a "Why & how to fix it" but I just do not see this coming up in the real world. Has this happend to you in a real world application? Just some more thoughts on the subject, great stuff though!

Reply to this Comment

Dan,

You are correct. This is not the most relevant real world solution. I guess the places that his happens, I can actually decrease the timeout of something like a CFExecute tag or a CFHttp tag and then if it throws an error, or if I CFTry/CFCatch the error, I can deal with it then.

In fact, on things like Reports where I have loooong running queries, this is actually how I deal with it.

I guess my concern is the "outliers" where something crazy is going on and my system attempts to log the error by it cannot do that as the logging itself takes too much time.

But that said, you are correct... this is not going to be an every day occurence. Something about it just seems strange. But I guess it makes sense. You could theoretically put an infinite loop into your OnError() method. So you need some sort of check to handle that. My gut says that it should have it's own request timeout or something.

Reply to this Comment

I asked a similar question to a great CF mind once and the response: "Don't do anything that would throw an error in your onError" -- meaning basically keep it simple. At most I'd shoot off an email in the onError method. Also a try with an empty catch should at the very least not show any exception info to your users, no?

Reply to this Comment

Todd,

I agree with you. I have actually found that any errors that occur in OnError() just error out and are not caught. However, the real problem here is that the error is not of a poor-programming nature; it's due to a lack of time.

I wonder if CFMail would have time to fire off. I will give that a test.

Reply to this Comment

A couple of comments.

1) According to the documentation and a bit of my testing, if an exception is thrown in the onError function, it is thrown to higher exception handlers, first <cferror...> tags then the site-wide error handler.

2) You can put plain HTML in the onError function an it will work in this situation. So one can output a simple message. I have not tried much else yet.

Reply to this Comment

Hi All,

I was having the same problem that you're describing and found that I could process the timeout through onError if the first thing I did in onError was to increase the timeout period.

In my case I increased the timeout in onError to 40 seconds which is 10 seconds more than is specified in the administrator. This gives me 10 seconds to handle any code (e.g. cfmail).

I know it's not very elegant but what the hell :-)

<cffunction naame="onError">

<cfsetting requesttimeout="40">

<cfmail ...>

<cfdump var="#cferror#">

</cfmail>

</cffunction>

Reply to this Comment

Hi Ben,

I know this thread hasn't been touched in almost a year, but I'm out of ideas. I can't get the TIMEOUT parameter of CFQUERY to work for the life of me. I'm using CFMX 7.0.2 with MS SQL Server 2005. This is what I run:

<cfquery name="test" datasource="dsn" timeout="1">
select top 20000 *
from table </cfquery>

<cfoutput>#cfquery.ExecutionTime#</cfoutput>

That query outputs a number that's in the 3000s -- which should never happen, right? Since the timeout is set for 1 second (or 1000ms). Why is this happening? I thought the TIMEOUT paramter works with SQL...

Reply to this Comment

@Sung,

I didn't even know the CFQuery tag had a timeout attribute :) If this is not working, it's probably a driver issue. Maybe this only works for certain types of datasources.

Reply to this Comment

Hi Ben,

It's news to me, too -- I didn't realize CFQUERY had a timeout param, either, until today. But I've scoured the net every which way, and it seems like there are a bunch of folks who say this works for JDBC drivers that hook into MS SQL. Which is exactly my setup, so I just don't get it.

I can't even use CFSETTING here. Because if the datasource is unresponsive, even if you have CFSETTING's REQUESTTIMEOUT set to 30 seconds, the page will not time out at the 30-second mark because this is an external action (like CFHTTP). It'll just keep waiting, and when the datasource finally fails, then the CFSETTING will kick in immediately. It's actually quite useless.

Reply to this Comment

@Sung,

From what I understand from my own experience, CFSetting only comes into play when control is back in the ColdFusion workflow. So, if a query is taking too long to run, the page won't timeout until the query returns and then ColdFusion tries to take a next action.

Reply to this Comment

@Ben Nadel, yup, I understand it the same way. That's why the TIMEOUT parameter in CFQUERY is really the only way to handle this -- except it just plain isn't working. It's frustrating...

Reply to this Comment

Hi Ben -any chance you have figured this out ? I am simply trying to loop over 5k records and append the key field to a list. it tiems out every time :(
Just hoping you figured out something.
thanks and God Bless
Chris

Reply to this Comment

@Chris,

For pages that I know are going to run a long time, I use a larger timeout (via the CFSetting tag). If the onError() does get fired, I typically include *another* CFSetting tag that increases the page timeout in order to allow the error-processing to take place.

Reply to this Comment

came across yer article and remembered this post from many moons ago - not many of the timeouts on cf are actually obeyed until the request completes. (Server: sorry about that request you submitted about 10 mins ago, but it told me that if it ran for more than 1 min, I should tell u it failed!)

http://forums.adobe.com/thread/264691?tstart=0

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.