Okay, fair warning Ben, this is going to be a bit long, but I know you’ll like it. It’s a summary of how my application uses task threads to do “fire and forget” CFHTTP sends of information to remote servers with hands-off, fully automatic periodic retries within an agreed-upon “transmission window” (as I describe above), with error handling and logging (which you inquired about!). Here goes:
Think of a task thread as a completely self-contained little thing that’s pre-loaded with every piece of data it needs (usually they’re all passed in as parameters up-front) so that it doesn’t need to access anything external… no variables or data structures from the request which launched it (which, for retries, is usually ended long ago anyway) or from the Application or other shared scope(s)… nothing outside of itself.
Except for files, that is. It can read and write files (provided you either hard-coded the file paths/names in, or you passed the file paths/names in up-front in parameters when spawning the task thread).
So Ben, what I do is spawn a task thread with EVERYTHING that’s needed to, say, send a CFHTTP transmission, and I have designated log files that the task threads can write to (log file paths/names passed in as parameters up-front). The task threads can write to the log file(s) upon successful or failed transmission, or if you prefer, only upon error. Whatever you like. But, whenever they want to put something in the log, a simple FileAppend() call does the trick. I also wrap the FileAppend() in a logfile-specific named lock with CFLOCK, to prevent multithreaded race condition problems, ESPECIALLY when several task threads are sharing the SAME log file (as opposed to them each having their own log file, again your choice).
So what it looks like is:
<cfthread name=“whatever” type=“task” action=“run” retryinterval=“(whatever you decide on!)” …
…followed by a lot of parameters, giving it all the data it will ever need to use as it runs, possibly repeatedly at different times, detached and all by itself, all under control of the Lucee Server which is following your instructions about retry counts and timings between retries… without you having to do anything (which is the main appeal).
Within the task thread code, to do the transmission, you just do a CFTRY, then inside that the CFHTTP, then a bunch of exception/error/problem-detection code, which CFTHROWs as necessary, which is CFCATCH’ed by a block below that namelocks the log file, FileAppend()s a nice informative message to it detailing what problem happened, then the named lock is released. (You can also do whatever other error-handling stuff you might need to do, here).
The next bit is interesting! At this point, having written a description of what happened to the log, you can now decide to either:
- Simply end this run/attempt of the task thread WITHOUT an active/unhandled exception, which terminates THIS run of the task thread, but leaves it active and thus eligible for automatic retries (if any still remain) after the proper waiting interval (although that’s all fully automatic and not your problem, ALL you have to do inside that CFCATCH block is just do a CFABORT (since you’re inside a CFCATCH block, the exception that got you into it has been handled and thus “doesn’t count” as an active/unhandled exception, so just doing a CFABORT is considered ending without any active/unhandled exception being in effect).
Or, you can:
- End this run/attempt of the task thread DELIBERATELY WITH an active/unhandled exception, which ends the task thread and makes it ineligible for any further retries (if any remain). No further retries will ever occur. To do this, instead of doing CFABORT inside the CFCATCH block, do a CFRETHROW followed by an (optional I guess, but I use it) CFABORT. This takes the exception that got you into the CFCATCH block, which was “handled” by that CFCATCH block, and now re-throws it so it’s now NOT “handled”, resulting in the task thread ending WITH an active/unhandled exception.
There are only two CFHTTP return values which should cause us to cancel all future retries: 200 (yay, success, we’re done, retrying would be pointless and stupid!) and 404 (target URL/module not found, retrying would be pointless and stupid!).
I end the task thread using method #1 when CFHTTP returns a status value of anything other than 200 or 404, because these are the transient problem situations where we DO WANT retries to occur.
I end the task thread using method #2 when CFHTTP returns a status value of 200 or 404, because these are the situations where we DO NOT WANT retries to occur.
Hope that helps! Throw a test task thread together, Ben, as I describe above, and play with it. You might love it like I do, for stuff like agreed-time-windowed CFHTTP transmissions and similar things!