Skip to main content
April 24, 2009
Question

Cleaner retry loop on cfquery?

  • April 24, 2009
  • 1 reply
  • 1035 views

Is there a cleaner or better way to do a retry loop on a flaky cfquery than something like below?

<cfset RETRY_COUNT_MAX=10>
<cfset RETRY_SLEEP_MS=3000>

<cfloop index="retryCount" from="1" to="#RETRY_COUNT_MAX#"><cftry>

<!--- do a cfquery or a cfhttp or anything that could be not work for a couple seconds --->

<cfbreak>
<cfcatch>
  <cfif retryCount GTE RETRY_COUNT_MAX><cfrethrow></cfif>
  <cfthread action="sleep" duration="#RETRY_SLEEP_MS#" />
</cfcatch>
</cftry></cfloop>

I do so many queries and passing of SOAP packets that I'd love some way to have code reuse.  I find myself not having a (necessary) retry loop just so my code is readable.

Here's my failed attempt using Custom Tag:

<cfif (ThisTag.ExecutionMode EQ "Start")>
<cfset retry_tag_index=0>
<cfelse>
<cfset retry_tag_index=retry_tag_index+1>
<cfif retry_tag_index LTE 10>
  <cfexit method="loop" />
<cfelse>
  <cfexit method="exittag" />
</cfif>
</cfif>

<cfimport prefix="tags" taglib="tags">

<tags:retryOnError>
<!--- do a cfquery or a cfhttp or anything that could be not work for a couple seconds ---> 
</tags:retryOnError>

But although I can loop with <cfexit method="loop" />, I can't seem to figure out how to do the necessary error handling in a custom tag.  I don't think it's possible.  Where would one even put the cftry?

Here's my less than useful attempt with cffunction:

<cffunction name="retryOnError">

<cfargument name="fn">

    <cftry>

<cfloop index="retryCount" from="1" to="#RETRY_COUNT_MAX#"><cftry>

     <cfset Arguments.fn()>

<cfbreak>
<cfcatch>
  <cfif retryCount GTE RETRY_COUNT_MAX><cfrethrow></cfif>
  <cfthread action="sleep" duration="#RETRY_SLEEP_MS#" />
</cfcatch>
</cftry></cfloop>

</cffunction>

<cffunction name="doTheQuery">

<!--- do a cfquery or a cfhttp or anything that could be not work for a couple seconds --->

</cffunction>

<cfset retryOnError(doTheQuery)>

Why less than useful?  The variables needed by the query are going to be out of scope, and passing each one would be more complicated that having a loop for each query. For example:

<cfloop query="qry">

     <cfset retryOnError(doTheQuery,lots of stuff needs passed in here)>

</cfloop>

Plus you can't declare cffunction inline in code execution if you plan on using cfthread because cfthread is considered a function (what an odd implementation) and you can't declare a function in a function.

I couldn't think of anything clean to even try with cfcomponent.

Any ideas?

    This topic has been closed for replies.

    1 reply

    Inspiring
    April 24, 2009

    What seems to be the issue with the original query/code that is causing you to find a work-around?  Is the problem that a query is taking too long?  Are your requests timing out?  How is the code being consumed - are you exposing the code as a web service, trying to use the code to process a remote web service result, calling it as a local process, etc?  Are you trying to access resources that don't exist yet (e.g. a file-write command that takes too long)?

    Without understanding what you are actually trying to accomplish from your code, its hard to recommend a solution.

    - Michael

    April 27, 2009

    What seems to be the issue with the original query/code that is causing you to find a work-around?

    I'm not sure how this will help, but I'll share.

    Currently, the issue is that the JDBC connection to our ERP system occasionally is flaky with errors of which

    "[Macromedia][SequeLink JDBC Driver]Network problem, session aborted due to internal error in remote procedure call, connection closed."

    is only one of a few.  They occur for various reasons from row locking that we ignore with cftransaction, license limts despite having plenty, connection limits that seem to ignore connection limit settings, and other just plain bugs that we seem to have no control over.  The only solution I know of is to just try again in a couple seconds.

    Currently, also an issue which I expect will come up is the first time our Internet goes now or is overloaded, I will get a bunch of failures of cfhttp SOAP packet calls that I will have to then put significant effort into repairing.  I'd like to put a retry loop on them too (at least the queries).

    Prior to now, the issue was with permission scaping some custom generated graphics code from a business partner with a flaky website via cfhttp.

    It seems to be coming up often, but I can't seem to find a clean way in Coldfusion code to do a retry loop without at least a half a dozen lines of code.

    Inspiring
    April 27, 2009

    Might be a good job for <cfschedule> - the only drawback is that the tag only handles scheduled executions of .CFM templates (as opposed to inline code).  Not sure if you've looked at it yet for your purposes.