Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Elliott Sprehn
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Elliott Sprehn

Programmatically Deleting Scheduled Tasks In ColdFusion

By
Published in Comments (20)

Inspired by Mike Schierberl's use of Java to programmatically delete ColdFusion scheduled tasks, I decided to try and to accomplish the same thing with plain old ColdFusion. I have tried to do this in the past, but always failed (and ended up creating a phantom task on the server - yikes!). I would have never been able to do this without Mike's absolutely critical explanation:

The Adobe techNote is somewhat misleading, it turns out that delete does work through the cfschedule tag, but the problem arises when the scheduled task tries to delete itself.

That is a really, really important note indeed. But, I would like to take that one step further: A scheduled task cannot be deleted while it is still running. Well, at least not programmatically I think.

So, the trick here, is to be able to get a scheduled task to call another page that then deletes the scheduled task, AND this has to be done only after the scheduled task has stopped running. Sounds complicated? It's actually very simple to do and it all comes down to page timeouts.

Let's take a quick look at the code. Here is the page getting called by the scheduled task:

<!--- Make sure the page can only run for 5 seconds. --->
<cfsetting
	requesttimeout="5"
	/>


<!--- Get this directory. --->
<cfset strDirectoryUrl = GetDirectoryFromPath(
	"http://#CGI.server_name##CGI.script_name#"
	) />

<!---
	Call execute task page. This is the page that will delete
	the currently running task. Put in a CFTry / CFCatch blocks
	because you never know how CFHttp might mess up.

	Tell the CFHttp tag to only allow ONE second of timeout
	time before it gives up waiting and continues on with the
	page processing.
--->
<cftry>

	<cfhttp
		url="#strDirectoryUrl#execute_task.cfm"
		method="GET"
		useragent="Mozilla/5.0"
		timeout="1"
		/>

	<cfcatch>
		<!--- An error occurred. --->
	</cfcatch>
</cftry>


<!---
	Trace the execution to a file just so we can make sure we
	know exactly when the task is running.
--->
<cffile
	action="APPEND"
	file="#ExpandPath( './trace.txt' )#"
	output="BT2 - Executed: #TimeFormat( Now(), 'hh:mm:ss:l' )#
	addnewline="true"
	/>

Now, let's take a look at the execute task page that deletes the scheduled task:

<!---
	Get the currently executing thread and force it
	to sleep for 10 seconds.
--->
<cfset CreateObject(
	"java",
	"java.lang.Thread"
	).CurrentThread().Sleep(
		JavaCast( "long", 10000 )
	) />


<!--- Kill task. --->
<cfschedule
	action="DELETE"
	task="BenTestTask2"
	/>

This all works quite nicely. And, as I said before, it's all thanks to page timeouts.

Take a look at the execute task page. Notice that the first thing the page does is sleep (thanks Mark Mandel's code example for asyncHttp) for 10 seconds. Then, after sleeping, it deletes the task. Now, look at the original task page. Notice that it only allows 5 seconds for page execution and the MINIMAL amount of timeout for the CFHttp request (one second) to wait for a response. This means that the calling page (the original scheduled task) should either finish executing OR timeout in some way before the Sleep() on the target page ever finishes. The bottom line: we can be sure that by the time the CFSchedule tag is executing, the original page has is NO LONGER RUNNING.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

Nice work Ben, nice to finally understand what the root of the problem was. Hopefully this will be fixed in Scorpio (although I don't have my hopes up considering they knew about the issue in CF6)

Couple of things I would add...

1) may want to consider adding throwonerror=no to the cfhttp tag. You won't see the error because it is in a scheduled task, but I think it would throw an exception every time

2) may want to think about implementing a lock to make sure that the scheduled task isn't running. Think of a scenario with a scheduled task running every 60 seconds. If your first request takes 58 seconds to process, it may start a new thread 2 seconds later. At this point the delete task is waiting 10 seconds, and when it tries to delete, the task will be running again. Unlikely, but it may cause some strange behavior.

Nice work!

15,848 Comments

Mike,

For starters, never could have done without your dynamite insights first. As for the throwonerror, I think it defaults to not throwing an error, however, it would be nice to see it explicitly written out.

As for the lock... I follow, that, would be cool. I figure I could put a NAMED lock on the entire scheduled task, then put a lock on th execute_taks.cfm page as well.... hmmm, but then one could lock the other and vice versa. I will play around.

9 Comments

Ben,

Very helpful post. Bit thanks to both you and Mike. Did you ever have time to investigate locking ideas?

15,848 Comments

I have not looked into the locking issue yet, but I have an idea for something even better. Hopefully I can post soon.

15,848 Comments

JM,

My new idea didn't work. I thought maybe I could do a page transfer (GetPageContext().Transfer()) to the page that would be deleting the task, but it did not work. The Transfer() method should kill the current page request, but I guess from ColdFusion's standpoint it's still the same thread or something. I don't know enough about what is going on underneath to understand why any of it doesn't work.

Sorry.

9 Comments

Ben,

This may be a dumb question but what is getPageContext().Transfer()? I only know about getPageContext().forward() and include(). Is transfer() like ASP's Server.Transfer()?

15,848 Comments

JM,

Sorry, that was my mistake. I did mean GetPageContext().Forward(). I don't know what Transfer is... i think I had something else on my mind at the time.

9 Comments

Ben,

No problem. You know I was reading Mike's blog entry and got to wondering about his comment: what happens if the scheduled task continues processing after the asynch http call.

I ran a few tests using CurrentThread().sleep() to force the scheduled task to continue executing for 30 seconds after the http call. The results I observed were that it created a phantom task.

Not that surprising I guess but it got me thinking about adobe's tech note 18361. Can you really delete a task through the CF Administrator if the task is still executing? So I created a recurring task that ran every 2 minutes. Then tried deleting the task in the ColdFusion Administrator (while the task was executing). The results were the same. It created a phantom task.

I'm curious if anyone else sees the same results or flaws in the test logic.

15,848 Comments

jM,

That's very interesting. I guess a task cannot be deleted through any method so long as the task is still running. Doesn't that seem strange? You would think that the thread running it would be totally separate from the definition of it... why should the two overlap?

9 Comments

Ben,

I ran some more tests. Stranger still is that "updates" seem to have no effect either, if you apply the changes while the task is running. I'm wondering if the thread executing the task is blocking changes or maybe the updating thread isn't communicating the changes to the running task. Without knowing about the internal workings this is all just guesswork of course. But if my tests are correct then you cannot even modify a task through the CF Admin, if the task is currently running. Thats a bit disturbing..

15,848 Comments

jM,

Nice testing! Yeah hopefully this is something that they can fix in the upcoming versions. It has been known about (to a degree) for some time now.

9 Comments

Ben,

Yes its definitely been around for a while. I am bit surprised that I haven't read more about the issue as it relates to any attempts to change a running task. The technote only mentions a problem with deletes. The neocron.xml file problem seems to have been fixed but the rogue task behavior still persists.

Like you said, hopefully it will get fixed in newer versions. It would also be nice to see some extended task information like "last run on date" or the "next scheduled date"... and of course a "kill" task feature might be nice ;)

15,848 Comments

@Scott,

That is a cool idea. The only thing worries me about it is that the path the XML file is hard coded. Other than that, very cool.

37 Comments

@Ben

that's a good point. I wouldn't normally hard code something like that either, but it was just a simple proof of concept for a blog entry, so I try to keep them as straightforward as possible. If I were really in the need for something like that, I would probably make a custom tag, or more likely these days a CFC, that could add, edit, and delete the scheduled tasks and I would use the #server.coldfusion.rootdir# to get the coldfusion root folder in my cffile tags. And now that I'm thinking about it I would put named cflocks around the process to protect the integrity of the file. Perhaps I should add a little note about that on my entry for people that don't yet know the potential risks with editing these kinds of files.

15,848 Comments

@Scott,

Yeah, that sounds like a good solution. I forgot about the SERVER-scoped settings. I need to take another look at those to see what googies can be leveraged.

37 Comments

There's not too much that's usefull, rootdir is about the only one I ever really use. here's all of the server variables as of CF8:

server.COLDFUSION.APPSERVER
server.COLDFUSION.EXPIRATION
server.COLDFUSION.INSTALLKIT
server.COLDFUSION.PRODUCTLEVEL
server.COLDFUSION.PRODUCTNAME
server.COLDFUSION.PRODUCTVERSION
server.COLDFUSION.ROOTDIR
server.COLDFUSION.SUPPORTEDLOCALES
server.OS.ADDITIONALINFORMATION
server.OS.ARCH
server.OS.BUILDNUMBER
server.OS.NAME
server.OS.VERSION

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel