Extending The Current Request Timeout In ColdFusion / CFML
The request timeout determines how long the current request / CFThread
can execute before the request throws a timeout exception. For the most part, this value is either never set (which then uses the default timeout defined in the ColdFusion admin); or, it's set once at the top of the request. Sometimes, however, I run into situations where I need to dynamically update the timeout of the current page. Unfortunately, the CFSetting
tag doesn't support this; as such, I wanted to outline ways in which this can be hacked into the request in either Adobe ColdFusion or Lucee CFML.
ASIDE: Over on the Lucee Dev Forum, I put out a suggestion that the
CFSetting
tag add an "extend" attribute to perform this operation. I'm not married to the idea - it was just a thought.
When you use the CFSetting
tag to set the timeout for the current request, you're setting an absolute value, not a relative value. Which means, if your request has already been running for 5-seconds, and then you set the request timeout to be 10-seconds, the new timeout threshold for the request will be 10-seconds - not 15 (which would have been 5 + 10).
To see this absolute value in action, we can attempt to increment the timeout value while making CFHttp
requests that we know will exceed the defined threshold:
<cfscript>
requestSetTimeout( 5 );
blockForSeconds( 4 );
// CAUTION: !! DOES NOT WORK !!
requestSetTimeout( 5 );
blockForSeconds( 4 );
writeOutput( "Done" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I set the request timeout to the given seconds using the native CFSetting tag.
*/
public void function requestSetTimeout( required numeric timeoutInSeconds ) {
cfsetting( requestTimeout = timeoutInSeconds );
}
/**
* I block for the given duration in seconds by using HTTP requests that block for one
* second. By performing this blocking in 1-second increments (essentially), this gives
* the overall page request more opportunities to step in and throw a timeout.
*/
public void function blockForSeconds( required numeric durationInSeconds ) {
for ( var i = 1 ; i <= durationInSeconds ; i++ ) {
// Will block for 1-second on target page.
cfhttp(
result = "local.apiResponse",
method = "get",
url = "http://#cgi.server_name#:#cgi.server_port#/request-timeout/block-and-lock.cfm"
);
}
}
</cfscript>
This code might look like I'm increasing the timeout by 5-seconds before I make my long-running requests; but, again, that's not how the CFSetting
tag works. As such, when we run this ColdFusion code, we get the following errors (truncated):
Lucee CFML:
RequestTimeoutException
: requestindex.cfm
has run into a timeout (timeout: 5 seconds) and has been stopped. The thread started 5028ms ago.Adobe ColdFusion:
RequestTimedOutException
: The request has exceeded the allowable time limit Tag:cfhttp
.
That second call to set the request timeout was, essentially, a no-op since I was setting it from 5
to 5
.
In order to extend the current request timeout, our subsequent calls to the CFSetting
tag have to set an increasingly large absolute value. Unfortunately, this isn't obvious; and, there's no single way to do this cross-platform (that I know of). But, it can be finagled in both Lucee CFML and Adobe ColdFusion through the use of the getPageContext()
system function.
Here's an updated version of the above approach; but, instead of calling requestSetTimeout()
a second time, I'm calling requestExtendTimeout()
! This method attempts to encapsulate the cross-platform complexity.
<cfscript>
requestSetTimeout( 5 );
blockForSeconds( 4 );
// NOTE: I'm EXTENDING here, NOT setting.
requestExtendTimeout( 5 );
blockForSeconds( 4 );
writeOutput( "Done" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I EXTEND the request timeout by the given seconds using the native CFSettings tag.
*
* CAUTION: In Lucee CFML, this extends the existing request timeout. In Adobe
* ColdFusion, this extends the current execution time of the request.
*/
public void function requestExtendTimeout( required numeric timeoutInSeconds ) {
var pageContext = getPageContext();
var requestTimeout = pageContext?.getRequestTimeout();
// ADOBE COLDFUSION does NOT support request timeout functions in the page
// context. As such, we'll have to go a bit more low-level.
if ( isNull( requestTimeout ) ) {
var startedAt = getPageContext().getFusionContext().getStartTime();
var startedSecondsAgo = now().diff( "s", startedAt );
cfsetting(
requestTimeout = ( startedSecondsAgo + timeoutInSeconds )
);
// LUCEE CFML allows request timeouts to be configured on the page context.
} else {
cfsetting(
requestTimeout = ( ( requestTimeout / 1000 ) + timeoutInSeconds )
);
}
}
// ... truncated - other two methods are the same in first example ... //
</cfscript>
As you can see, I attempt to call pageContext?.getRequestTimeout()
which will fail gracefully on Adobe ColdFusion thanks to the safe navigation operator. I use the result of this call to determine which platform I'm on; and, then, branch into a platform-specific approach for calculating the new request timeout.
And, when we run this in both Lucee CFML and Adobe ColdFusion, we get the following output:
Done.
The request timeout was successfully extended to 10-seconds on both CFML platforms, allowing 8-seconds worth of CFHttp
calls to execute. I don't love having to reach into the Page Context in order to accomplish our goals here. But, to be fair, I don't have to extend the request timeout very often.
coldfusion.runtime.RequestMonitor
Class
UPDATE: Using the Right after I published this article, I came across another article that I wrote in 2007 dealing with the same exact topic. In that article, I use the internal ColdFusion class, coldfusion.runtime.RequestMonitor
to get the current request timeout. And, unlike the getPageContext()
approach that I used above, this RequestMonitor
class appears to work in both Lucee CFML and Adobe ColdFusion.
Here's an updated demo that gets the current request timeout from this Java class:
<cfscript>
requestSetTimeout( 5 );
blockForSeconds( 4 );
// NOTE: I'm EXTENDING here, NOT setting.
requestExtendTimeout( 5 );
blockForSeconds( 4 );
writeOutput( "Done" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I EXTEND the request timeout by the given seconds using the native CFSettings tag.
*/
public void function requestExtendTimeout( required numeric timeoutInSeconds ) {
// CAUTION: Your ColdFusion platform needs to have permissions to access Java
// objects in order to use this (I think).
var currentTimeout = createObject( "java", "coldfusion.runtime.RequestMonitor" )
.getRequestTimeout()
;
cfsetting(
requestTimeout = ( currentTimeout + timeoutInSeconds )
);
}
// ... truncated - other two methods are the same in first example ... //
</cfscript>
This approach is more akin to the approach that I used with Lucee CFML.
Want to use code from this post? Check out the license.
Reader Comments
Ok, now I feel like I struck gold here! Great stuff! 🙌💥💯
@Chris,
Thank you, good sir! Sometimes it's fun to get into the weeds like this.
Sorry for asking but where that code is the best place to put it... application.cfm, application.cfc or in the scipt page
Many thanks for great support you are giving us
Mike Savoy
@Mike,
This type of code can go anywhere in the request flow. That said, I will often put the "default" request timeout in the
onRequestStart()
method in theApplication.cfc
ColdFusion framework component. This way, I can set an expectation for every request coming into the application.Then, I will sometimes override that value lower-down in the request routing when I determine that I'm about to execute something will take longer that than the expected default request timeout.
And, to be honest, I'm not 100% married to the place this goes. I sometimes think I should be putting all of this decision logic into the "Controller" layer. But, sometimes, the logic really only makes sense down in the "Business logic" layer. Honestly, I don't stress too much about putting it in the right place as long as I have a good in-code comment as to why the request needs a longer timeout. I can always move it to another part of the request if it feels like it's in the wrong place.
Many thanks for answering my question... Very nice and appreciated. I will follow your instinct.
Thanks
Mike
@Mike,
Of course, any time! 💪 One great example of this is for use in scheduled tasks - the background tasks that ColdFusion provides. Often times, we use scheduled tasks to run large calculations or generate documents (really, do any kind of "heavy lifting"). It's great to be able to set a low default request timeout for the application at large; and then, for the area of the app that runs scheduled tasks, have that use a larger timeout (or even a per-scheduled task timeout).
It's great that ColdFusion makes things so flexible. But, that it also makes you think about what is a "healthy" workflow for the server. A nice trade-off.
Hi Ben,
We are running ColdFusion 2016 enterprise and having a hard time trying to increase the request time out for a process. The main issue I want to tackle is during the first load of an app, onApplicationStart takes a lot of time (close to 6 minutes) and I don't want to set the timeout time to be this high in ColdFusion administrator setting. I have tried using <cfsetting requestTimeOut = "600"> in the start of onApplicationStart function but it doesn't seem to have been working as request still times out. I am quite sure the request is actually not taking more than 400 seconds. Any idea what the issue can be and what can I do to fix the issue?
@Joe,
That's really odd - I would have totally expected setting the
<cfsetting>
tag in theonApplicationStart()
to have worked. That fact that it is not is surprising to me.At this point, I'm just guessing. But, I wonder what would happen if you put the really large
<cfsetting>
in the component "pseudo constructor" (ie, inside thecomponent
tag, but outside of any functions). Then, in theonRequestStart()
, revert back to a lower one. Like:I am not sure this is a good idea -- and you probably will get a timeout error on the first try. But, at least the application may bootstrap.
With all that said, 6-minutes to bootstrap the application is really long time. Do you have a sense of what is actually going on there? Is the bottleneck in your code? Or in the underlying server start-up time?
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →