When should cfthread be used? - coldfusion

Would you please give a simple scenario where threading is necessary?
Thank you,
Nich

I used it in a situation where we needed to initiate some back end data processing to tabulate data prior to the user executing some reports. So after login we would fire off a task using cfthread to build the warehouse data for the user. Worked great!
So think of cfthread as a way to execute an asynchronous task on demand. Extremely useful in the right situation!

Use cfthread if it makes sense to execute certain code in parallel with the main request processing code.
Example: say you are uploading a directory of files and you need to do the same processing to each, perhaps to save the contents into the database. You could then use cfthread to run the processing on each file asynchronously. Consider this pseudocode:
<cfdirectory directory="x" action="read" name="allFiles" />
<cfloop query="allFiles">
<cfthread action="run" name="thread-#allFiles.name#>
<!--- Read your file in and do processing --->
</cfthread>
</cfloop>
Remember there is a setting CF Admin to set the number of threads you can spawn in this way! A lot will also depend on your system resources.
There are other examples around which describe other use cases in greater detail. Just do a Google search. I'd recommend Ben Nadal's cfthread primer as a good starting point.
One other warning: threads are a solution to all problems! I was using them to deal with queue processing before and ran into trouble.
In summary cfthread is a great feature of ColdFusion, enjoy it!

I have used cfthread in two situations.
As noted above, I've used it when I wanted to do asynchronous processing while my main thread was doing other things. For example, I used it to load RSS data while generating the rest of the page. Since the call to the RSS source took a couple of seconds, I started a thread before beginning any other processing. It ran while I was querying, drawing the layout, etc. Then I joined the thread and displayed the RSS data. This prevented me from having to pause the page load while the RSS was populating.
The second way I've used this is sort of an on-demand alternative to using the scheduler. We were generating complex PDF documents. The person generating them didn't need them immediately, so rather than have the user stuck while we generated the document, we created an unjoined thread to process the PDF. We then restricted the number of cfthreads being processed at any ppoint. Now, regardless of load, the cfthreads would simply queue, and be processed as resources were available.

<!--- store value into message varaible --->
<cfset variables.message = "It's orginal value.">
<!--- create new thread --->
<cfthread name="ThreadOne">
<!--- overwrite new value into existing variable. --->
<cfset variables.message = "It comes from thread.">
</cfthread>
<!--- join thread --->
<!---
If we leave this join, the code within thread will execute but won't display the value.
--->
<cfthread action="join" name="ThreadOne" />
<!--- Output --->
<cfoutput>#variables.message#</cfoutput>
Check above coding. It's kinda clear.

Related

Counting the number of hits in a life cycle method using ColdFusion

I am needing to display some analytics regarding how many times a "OnRequestStart" function is called, or rather the number of hits per hour on our internal API that’s contained in a .cfc file. Preferably through the life cycle method; however the counter must continue outside of the life cycle. I know this can be done easily in other languages, but I am new to ColdFusion and have been trying to read through the documentation to see if there is some form of life cycle method I can use in order to achieve this. If there are any sort of documentation I am missing (I've tried learn cf in a week, cfdocs, adobe documentation), but there's not really much out there. This probably isn’t 100% clear, but if there is any clarification needed I’ll be happy to help.
Edit: I figured it would be best to set an application variable in onApplicationStart and incrementally add 1 to the counter variable within the onRequest start. Here is my example code:
Application.cfc:
<CFFUNCTION NAME="OnApplicationStart" ACCESS="PUBLIC" RETURNTYPE="BOOLEAN">
<cfset Application.timer EQ 0/>
<cfset Application.counter EQ 0/>
</CFFUNCTION>
somepage.cfm
<tr>
<cfoutput> #Application.counter#</cfoutput>
</tr>
I thought this would work, but I get an error saying Element COUNTER is undefined in APPLICATION. Is there something I am missing? I tried to restart the CF server service and web server, but no luck.
Thank you everyone for your help
Write it to the application scope, in onRequestStart(), include the following code:
lock scope="application" type="exclusive" timeout=1 throwontimeout=false {
if (!application.keyExists("reqCount") {
application.reqCount= 0;
}
application.reqCount++;
}
Then you can use it whereever you need it.
Turns out, the simplest way to do this is to simply create a variable either outside of the lifecycle or within onApplicationStart then increment the variable with each onRequestStart. You can then do whatever is needed after that. One thing I did stupidly was put <cfset Application.timer EQ 0/> <cfset Application.counter EQ 0/> after a <cfreturn> tag. Lesson learned, don't assume anything, research everything haha.
Thank you everyone

Why doesn't code after cfreturn execute in a CFC?

I have created a CFC to process and return comments posted back to the submitting page. It just returns a JSON string of the comment made back to the browser which is then inserted into the DOM using jQuery.
However, I also want to send an email to notify the author that a new comment has been posted. I want to do this AFTER the JSON has been returned to the browser because it will speed up the user interface than having to wait for an email to be sent before updating the DOM.
I am currently testing this and can't see that ColdFusion will execute anything after a <cfreturn> tag. For example the following is not working for me:
<cffunction>
....
<cfreturn NewComment/>
<!--- Anything after the cfreturn above doesn't seem to get executed --->
<cfmail to="somebody#domain.com" from="nobody#domain.com">
A new comment is available for you to read
</cfmail>
</cffuntion>
However this DOES work:
<cffunction>
....
<cfmail to="somebody#domain.com" from="nobody#domain.com">
A new comment is available for you to read
</cfmail>
<cfreturn NewComment/>
</cffuntion>
What's the deal with this and how could I achieve what I'm trying to do? If I wanted to do a whole lot more than just send an email then the DOM would have wait a significant amount of time before being updated which makes the user experience sluggish.
A return ends processing of the function. Anything after a return is not processed.

How can you get a list of the datasources used to generate a page in coldfusion?

When you add a debug IP to review the debug information from the collection of templates that are parsed to present the page, it includes a list of all queries from that page.
Is it possible to get access to that object for inspection?
I'm looking at trying to automate the documentation which data sources are in use for which page requests. We have a large number of small web apps that access different databases and have different dependencies. I'm hoping to find a way to automate the documentation of these dependencies rather than having to manually review all code for all of the webapps.
Not sure if the object doesn't get created until after the page creation is too far gone to actually do anything with the data, but who knows...
Here is a snippet of code that you can add to the end of your template to get a list of datasources used on the page:
<cfobject action="CREATE" type="JAVA" class="coldfusion.server.ServiceFactory" name="factory">
<cfset cfdebugger = factory.getDebuggingService()>
<cfset qEvents = cfdebugger.getDebugger().getData()>
<cftry>
<cfquery dbtype="query" name="cfdebug_qryDSN">
SELECT DISTINCT DATASOURCE FROM qEvents WHERE type = 'SqlQuery'
</cfquery>
<cfcatch type="Any">
<cfset cfdebug_qryDSN = queryNew('DATASOURCE')>
</cfcatch>
</cftry>
<cfdump var="#cfdebug_qryDSN#" label="cfdebug_qryDSN">
PS: most of the inspiration for this snippet came from {cfusion 10 home}\cfusion\wwwroot\WEB-INF\debug\classic.cfm. You can get some good ideas on how to gain access to debugger objects/data from this file.
For anyone stumbling across this....
If your [cfroot]/cfusion/lib/neo-datasource.xml file is WDDX encoded and you're not sandboxed, you can use the following (tested on CF2021)
<cflock type="readonly" scope="Server" timeout="5">
<CFSET LibPath=Server.System.Properties["coldfusion.libPath"]>
</cflock>
<CFFILE action="Read" file="#LibPath#/neo-datasource.xml" variable="DatasourcesWDDX">
<cfwddx action="wddx2cfml" input="#DatasourcesWDDX#" output="Datasources">
<cfoutput>#StructKeyList(Datasources[1])#</cfoutput>
<cfdump var=#Datasources#>
The first position of the Datasources array holds a structure containing information on each configured datasource with the main key being the name of the datasource.
Here's an idea that'll work for each application which uses an Application.cfc.
Enable Request Debugging Output in CF Administrator.
Configure Debugging IP Addresses so that every page receives debugging information.
Assuming that Select Debugging Output Format is set to classic.cfm, short circuit {cfusion 10 home}\cfusion\wwwroot\WEB-INF\debug\classic.cfm by making <cfreturn> the first executable statement in classic.cfm. This will prevent any pages from seeing the debug output.
In Application.cfc::OnRequestEnd() do what Scott Jibben suggested. You can wrap Scott's idea in an <cfif IsDebugMode()>.

cflocation no longer working in CF10

I’ve been battling with a bug with cflocation ever since I upgraded from CF9 to CF10. I’ve worked on it a number of times but always give up but today it’s finally annoyed me enough that it’s time to solve the problem (I hope).
Firstly, apologies as I have tried to write a simple test case to reproduce this problem but so far I’ve been unable to reproduce it outside of original code.
I have a multi-step import process that should run overnight. The process is made up of about 10 files that chain together. When calling the process via a scheduled task a URL parameter (scheduledtask) is also passed. If the parameter does not exist, at the end of each step it stops and waits for the user to click a link to the next step. If the parameter does exist it uses cflocation to move to the next step.
Of all the steps, the 2nd one is the most complex and time consuming (data from multiple web service requests that then need joining, cleaning and inserting into a database). It’s this second step that has a problem after moving to CF10. The page seems to work fine as it’s getting to the very bottom of the page where the cflocation tag is but it never fires cflocation. I’ve added in a cfmail tag to email me as the cflocation is being called, which is always sent, but again cflocation doesn’t fire.
It can’t get much more basic than:
<cfif scheduledtask EQ "true">
<cfmail from="xxxxx” to="xxxx” subject="About to call duplicate" type="text/html">
<p>calling duplicate check - scheduledtask</p>
</cfmail>
<cflocation url="importDupCheck.cfm?scheduledtask=true" addtoken="false">
</cfif>
I know that there is a problem with cfflush and cflocation and I’ve already checked for that.
As a test I tried moving the above logic up the page to just after all the data is retrieved by the webservice but before it’s processed and this time the cflocation worked. I thought maybe it’s a timeout problem where the scheduled task engine has given up waiting even though the page is being allowed to fully load. I tried testing this by having a basic file that had:
<cfset sleep(240000)>
* 4 minutes
before it called cflocation but it worked fine.
The other steps in the process are working as expected with cflocation, just not this one. All this code runs fine under CF9, just failing with CF10.
Does anyone has some pointers on where I should be looking?
Regards
Mark
* Update *
So I’ve now worked out how to easily reproduce this in a few lines of code. Turns out it has nothing to do with scheduled tasks.
index.cfm
<h1>Testing cflocation</h1>
<cfloop index="i" from="1" to="7000" ><!--- For me it stops working once the loop goes beyond 6808 rows --->
row <cfoutput>#i#</cfoutput>: abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789<br/>
</cfloop>
<p>Now loading resultpage via cflocation</p>
<cflocation url="resultpage.cfm" addtoken="false">
resultpage.cfm
<h1>Made it!</h1>
<p>Reload the first page</p>
In my testing I found that if I looped 6808 times it works but 6809 times fails. With 7000 records we are only talking about a 36 KB file….why is this failing only in CF10?
One last thing I should note in case it has an impact- everything I do is done via https.
Ok I worked it out
In CF Administrator
Maximum Output Buffer size 1024
After which a flush will occur which will stop cflocation working.
Increase it and it works.
I have no idea why your code as it stands doesn't work, I'm afraid. At a guess, it's down to ColdFusion 10's replacement of the scheduled task engine, and it not liking some vagary of client-side redirects.
However maybe you can bypass the situation anyhow.
ColdFusion 10's scheduler has a concept of chaining tasks, so that when a task finishes, another one is kicked off. You could conceivably revise your scheduler to leverage that instead?
I had a similar issue, but I was using the location() function in a cfscript. But I suspect the same principle applies.
Prior to the location() function, we were calling a function that was not wrapped in <cfsilent> tags. As a result, every line of the function, including loops, was being output as an empty line of HTML. When I viewed the source code, there were over 20,000 lines of empty HTML code. That is probably enough to reach the 1024 kb buffer limit and trigger the flush that aborts the redirect.
By adding <cfsilent> tags to the custom function, I reduced the number of empty lines of HTML to 39, and now the location() function works as expected.
To determine if this is the issue you are having, add abort; (or <cfabort>) just ahead of the location(), then view the source code of the resulting - presumably empty - page.
Have you tried adding CFABORT after CFLOCATION? That's what I always do - as the code after CFLOCATION should not be executed.
If you check the response from the server (using Charles proxy, Fiddler or something similar) you may find something in the HTML/JS that's preventing the redirection.

Cfcfhart within cfthread possible? how to get output?

I'm trying to improve the execution time of one of my reports which uses cfcharts to print graphs. The code itself is a loop of entities and for each entity I create a chart (it's a comparison report).
By doing this inline it takes almost a minute or more to complete due to the complexity of the report so I'm trying to use cfthread for this case but I'm not sure if it's possible.
Here's the code:
<body>
<cfloop array="#uuids#" index="uuid" >
<cfthread action="run" name="t#threadCount#" output="to#threadCount#">
...
<cfchart >
...
</cfchart>
</cfthread>
...
</cfloop>
</body>
As expected cfchart will not be "printed" inside cfthread though apparently it's being executed. How can I get the output of cfthread? One possible solution would be creating an image from cfchart and just use the image to build a document at later time when all threads finished but I was wondering if there's any way of getting the cfchart output from cfthread.
Try putting a custom tag around the cfchart call and capturing generatedcontent into a variable - then access it using the threads scope. I'm not positive this will work (depending on your output format).
I have not tested this, it's just an idea, but you could try putting cfchart inside of a cfsavecontent block.
I was able to use cfsavecontent to save and then generate cfcharts with quotes.
Just make sure whatever queries / data variables used are saved inside the cfsavecontent block (I was having trouble with this part; scoping gets a little wonky if you aren't careful)
<body>
<cfloop array="#uuids#" index="uuid" >
<cfthread action="run" name="t#threadCount#" output="to#threadCount#">
<cfsavecontent variable="thisContent">
<cfquery name="thisQuery" datasource="dsource">
...
</cfquery>
<cfchart >
...
</cfchart>
</cfsavecontent>
</cfthread>
...
</cfloop>
<cfloop array="#uuids#" index="uuid" >
<cfthread action="join" name="t#threadCount#"/>
#thisContent#
</cfloop>
</body>
My issue that I had with this was the dynamic variable naming / calling. I'm sure that's a very easy fix, but if I called each content by the name I knew would be created, it was displayed--though this defeats the purpose of using dynamic variables. But that's another battle entirely! The answer is yes, you can use cfsavecontent with cfcharts inside a cfthread.