CFSetting RequestTimeout Updates Timeouts, It Does Not Set Them
That title probably doesn't make a lot of sense. What I mean by that is that when you use a CFSetting tag and set the RequestTimeOut attribute, the value you assign does not just apply to everything going forward - it applies to everything going forward in the context of everything that has already happened. Ok, maybe that doesn't clear anything up. What I mean to say is that the RequestTimeOut attribute does not determine the amount of time left, it determines the amount of processing time allowed in total.
Here is a demo to help explain what I am saying. For this example, I use a ColdFusion user defined function named KillTime(). This function simply runs a loop until a given amount of time has passed:
<cffunction
name="KillTime"
access="public"
returntype="void"
output="false"
hint="I kill time for the given miliseconds.">
<!--- Define arguments. --->
<cfargument name="MS" type="numeric" required="true" />
<!--- Get start and end tick out values. --->
<cfset var intStart = GetTickCount() />
<cfset var intEnd = (intStart + ARGUMENTS.MS ) />
<!--- Loop until this time is killed. --->
<cfloop condition="(GetTickCount() LT intEnd)">
<!--- Just try to kill some processing time. --->
<cfset intStart = (intStart * Pi()) />
</cfloop>
<!--- Return out. --->
<cfreturn />
</cffunction>
Now, with that function in mind, here is the test code:
<!--- Set the current time out to be 3 seconds. --->
<cfsetting requesttimeout="3" />
<!---
Get the millisecond start time for page processing
(so that later on, we can check to see how long
the page ran overall).
--->
<cfset intStart = GetTickCount() />
<!--- Try to kill some time. --->
<cftry>
<!---
Here, we are killing time - 4 seconds to be
approximate. This will exceed the request time
out set above (3 seconds) and will throw an error.
--->
<cfset KillTime( 4000 ) />
<!--- The KillTime() method call has timed out. --->
<cfcatch>
First Timeout!<br />
<!---
In an attempt to "recover" from this time out,
update the request time out to be six seconds.
--->
<cfsetting requesttimeout="6" />
<!--- Try to kill some more time. --->
<cftry>
<!---
We are going to try and kill about four
seconds. If this is in terms of the six-second
timeout set above, this should NOT timeout.
However, if this is in the context of the
overall page time (including previous kill time
calls), then this will timeout.
--->
<cfset KillTime( 4000 ) />
<!--- The KillTime() method has timed out. --->
<cfcatch>
Second Timeout!<br />
</cfcatch>
</cftry>
</cfcatch>
</cftry>
Total Time: #(GetTickCount() - intStart)#
That gives us the following output:
First Timeout!
Second Timeout!
Total Time: 6016
Now, if you look at the code, and the output, you will see that both calls to KillTime() threw timeout exceptions. The first one is straight forward - we tried to kill 4 seconds when the current request timeout only allowed 3 seconds. But, then we set the request timeout to be 6 seconds. In this second KillTime() request, we try to kill 4 seconds. If the previous 6 second timeout was a NEW timer, then this would not timeout (4 is less than 6). However, this second KillTime() method times out. This shows us that request timeout settings don't "add" time, they merely update how much total time is allowed.
Aside from the fact that this gives us a little more insight into using multiple CFSetting tags (which might not be totally obvious - it wasn't to me), it also proves that when a tag or block of code throws a request timeout exception, ColdFusion doesn't die right away - there is some wiggle room in which to recover or potentially add more execution time.
Want to use code from this post? Check out the license.
Reader Comments
This would make sense, Ben, as ColdFusion uses output buffering by default, and you must explicitly tell it to flush the contents of the buffer. This is unlike language like PHP, for example, which flush content out as it is processed, and you must explicitly tell it to buffer output if you wish for that behavior.
@Adam,
While I agree with you on the buffering feature, I am not sure how buffering relates the page timeout?
good to know, Ben. Thanks!
I cannot say with any authority, however I have spent a little time writing my own language interpreters, and found that when assembling my page into my output buffer anytime I found a command that would modify program execution settings, I would put those into my script's environment settings, and overwrite any existing value.
That is essentially what I figure ColdFusion is doing. This is a setting that affects this request's processing environment, and it isn't "queuing" them up, but simply storing the last value.
Once again, no authority, just assumtions based off behavior and my own code in the past. *shrug*
What happens if you remove the second requesttimeout setting? Since you've already caught the exception and handled it, how much more wiggle room does CF allow you?
If I take out the second CFSetting, this is the output:
First Timeout!
Second Timeout!
Total Time: 3031
Looks like the second KillTime() method times out immediately, is caught, and then lets the rest of the page process (which only takes a few milliseconds).
In my experience, the "wiggle room" is generally NOT enough time to do anything much like send an email that has much of any processing time to it (ex. CFDump var="CGI").
@Adam,
Ahhh, I see what you are saying now. Thanks for the clarification.
This is an interesting piece of code.
I would have expected that setting to tell the CF server how long to wait until pulling the plug on the request (kind of what it says on the box). I would not expect it to throw a catchable exception, because it should have ceased processing the request completely.
Good to know, in an esoteric sort of way. Good work.
--
Adam
<code>
<cfset thread = createObject("java", "java.lang.Thread") />
<cfset thread.sleep(5000) />
</code>
If what you are suggesting is that the second killtime() still dies because the original requesttimeout (3 seconds) has ended and therefore the second requesttimeout (6 seconds) won't be able to update any remaining time, why does making the second timeout 60 seconds rather than 6 work (not timeout) the second killtime()?