I have a web application that is generating hundreds of PDFs in batch, using ColdFusion 8 on a Windows/IIS server.
The process runs fine on my development and staging servers, but of course the client is cheap and is only paying for shared hosting, which isn't as fast as my dev/staging boxes. As a result, PDF generation threads are timing out.
The flow is something like this:
- Page is run to generate PDFs.
- A query is run to determine which PDFs need to be generated, and a loop fires off an application-scoped UDF call for each PDF that will need to be generated.
- That UDF looks up information for the given item, and then creates a thread for the PDF generation to work in, preventing generation from slowing down the page.
- The thread simply uses CFDocument to create a PDF and save it to disk, then terminates.
Threads do not re-join, and nothing is waiting for any of them to finish. The page that makes the UDF calls finishes in a few milliseconds; it's the threads themselves that are timing out.
Here is the code for the UDF (and thread creation):
<cffunction name="genTearSheet" output="false" returntype="void">
<cfargument name="partId" type="numeric" required="true"/>
<!--- saveLocation can be a relative or absolute path --->
<cfargument name="saveLocation" type="string" required="true"/>
<cfargument name="overwrite" type="boolean" required="false" default="true" />
<cfset var local = structNew() />
<!--- fix save location if we need to --->
<cfif left(arguments.saveLocation, 1) eq "/">
<cfset arguments.saveLocation = expandPath(arguments.saveLocation) />
</cfif>
<!--- get part info --->
<cfif structKeyExists(application, "partGateway")>
<cfset local.part = application.partGateway
.getByAttributesQuery(partId: arguments.partId)/>
<cfelse>
<cfset local.part = createObject("component","com.admin.partGateway")
.init(application.dsn).getByAttributesQuery(partId: arguments.partId)/>
</cfif>
<!--- define file name to be saved --->
<cfif right(arguments.saveLocation, 4) neq ".pdf">
<cfif right(arguments.saveLocation, 1) neq "/">
<cfset arguments.saveLocation = arguments.saveLocation & "/" />
</cfif>
<cfset arguments.saveLocation = arguments.saveLocation &
"ts_#application.udf.sanitizePartNum(local.part.PartNum)#.pdf"/>
</cfif>
<!--- generate the new PDF in a thread so that page processing can continue --->
<cfthread name="thread-genTearSheet-partid-#arguments.partId#" action="run"
filename="#arguments.saveLocation#" part="#local.part#"
overwrite="#arguments.overwrite#">
<cfsetting requestTimeOut=240 />
<cftry>
<cfoutput>
<cfdocument format="PDF" marginbottom="0.75"
filename="#attributes.fileName#" overwrite="#attributes.overwrite#">
<cfdocumentitem type="footer">
<center>
<font face="Tahoma" color="black" size="7pt">
pdf footer text here
</font>
</center>
</cfdocumentitem>
pdf body here
</cfdocument>
</cfoutput>
<cfcatch>
<cfset application.udf.errorEmail(application.errorEmail,
"Error in threaded PDF save", cfcatch)/>
</cfcatch>
</cftry>
</cfthread>
</cffunction>
As you can see, I've tried adding a <cfsetting requestTimeout=240 />
to the top of the thread to try and make it live longer... no dice. I also got a little excited when I saw that the CFThread tag has a timeout parameter, but then realized it only applies when joining threads (action=join).
Changing the default timeout in ColdFusion Administrator is not an option, as this is a shared host.
If anyone has any ideas on how to make these threads live longer, I would really appreciate them.