Coldbox - Scheduled Tasks (A Couple of Suggestions)

I’ve spent the last couple of days learning and understanding the new Coldbox Scheduler.

One of the first things I found is that when you call runEvent from the defined task, like:

task( "process-logs" )
	.call( function(){
		runEvent(event:"scheduled.station.processLogs", eventArguments:{"scheduled":true});
	})
	.every(5, "minutes")
	.delay(1, "minutes")
	.withNoOverlaps();
}

that runEvent does NOT fire interceptors. We toss a few things in the prc like prc.ServiceFactory and prc.Context (homegrown). I had to create a pre-handler in my “scheduled/station.cfc” to create those things for me. Kind of a duplicate of my SetupInterceptor. That’s okay, I didn’t mind too much, but I would suggest the documentation reflect that.

My second suggestion is this, in a round-about way. You can see I have an argument there. (Which I now know is a true arguments scoped variable!) I am creating the scheduled task that way so that I can also run it manually, if need be, and differentiate between a scheduled run and a manual hit-the-event-in-the-url execution.

You have that great .withNoOverlaps() feature. I think that’s pretty smart. I would say, though, it would be nice to have access to the information that must be stored somewhere indicating the task is running. If you have an “every minute” event that takes two minutes, the second execution must know it’s already in progress.

Yet, when I run this code:

local.oScheduler = controller.getSchedulerService().getSchedulers()[appScheduler@coldbox];|
local.stTask = local.oScheduler.getTaskRecord(arguments.task);|

The TaskRecord has no indication of whether its running or not. (It also, at least in my testing, does not show the “nextRun” time, it’s just blank in the dump.) But, again, it must be somewhere to prevent the overlaps and if it were included in the TaskRecord lookup… that would be cool.

It would be great for me to get that task that way, during my manual run, to determine if Coldbox is firing it right now, so I can NOT do a second run manually.

Just a couple of suggestions. But I really like it, and I’m glad I’m forcing myself to learn my way through it.

that runEvent does NOT fire interceptors. We toss a few things in the prc like prc.ServiceFactory and prc.Context (homegrown). I had to create a pre-handler in my “scheduled/station.cfc” to create those things for me. Kind of a duplicate of my SetupInterceptor. That’s okay, I didn’t mind too much, but I would suggest the documentation reflect that.

Which interceptors would that be? runEvent() has never fired any external interception points. I think you are confusing it with a normal event execution and runEvent() has NEVER been the normal default event. It’s just a pure executable of the requested event. The only interceptions are the localized interception points.

The TaskRecord has no indication of whether its running or not. (It also, at least in my testing, does not show the “nextRun” time, it’s just blank in the dump.) But, again, it must be somewhere to prevent the overlaps and if it were included in the TaskRecord lookup… that would be cool.

The nextRun was removed, it was never done. Even though @DaveL has volunteered (cough cough) to implement it. The overlaps are stored in CacheBox so many servers in a cluster can discover it. I think it can be done where the task record can request the cache record as well. Could be.

Just a couple of suggestions. But I really like it, and I’m glad I’m forcing myself to learn my way through it.

Keep them coming. This is brand new territory and we keep improving it in order to make this core solid first. Then I want to produce a nice UI to produce reports and stats and much more.

You’re right, Luis, I guess I am confusing it with a normal lifecyle event. Again, not a big deal, I was able to compensate, but I do think it would make a good addition to the one-page documentation you have on it, just so someone isn’t beating their head against the wall wondering why something isn’t in the rc/prc they might expect.

NextRun is fine, just an observation. The key is still there, though. Certainly not critical for my needs. What I did is, when I run manually, I’m disabling the task, and if it was originally enabled, re-enabling it at the end of the run (and in a catch block in case of errors). That’ll work for now.

I’ll try to dig around in Cachebox for the info (or the code that uses it). I need to flip some code over to Cachebox anyway that I have in place and am just using variables scope for at the moment. (Quick and dirty, cleanup after it works!)

I’m using an extensive processing system for scheduled tasks, batches, data, exceptions, etc. and I’m investigating how I can replace a lot of that with the Scheduler stuff.

Great job guys!

I haven’t forgotten, Luis! :slight_smile:
I was pretty busy putting together some important PRs for Quick to allow for single-table inheritance (STI). I haven’t heard back from Eric yet, but hopefully it will make for a nice addition.

I will be traveling next week but I plan on running some experiments to see if I can get nextRun to work. Date math is always a PITA, but I am up for the challenge.

Luis, it’s not super-duper awesome, just some quick hacky code, but I threw this together.

I’ll be glad to share my quick and dirty code.

I did learn one thing, though, while writing this:

local.oTask = controller.getSchedulerService().getSchedulers().getTasks()[specific task name]
local.oTask.future.getDelay("seconds")

The getDelay() return… it’s negative if the task is currently running. So that helps me a bit. Now I just need to throw moment.js and some countdown code so I can have a more real-time screen. (Like if it’s running (negative delay), I would block the enable/disable buttons for that entry. (And have the ajax function also ignore it when looping over Disable All / Enable All. At least until I create interruptable code.)

Let me know, I’ll email the code over to you if you want it as a starting place. I’m sure it’ll be improved over the next couple of days. I needed it so that I could edit code, reinit and prevent those scheduled items rom running.

Well, seemed great… until I realized there might be a bug.

I’m doing task.disable() and task.enable() and my UI (and the task properties) are showing it correctly, but it’s not keeping it from running.

Hmmm. My remote server is not working when I disable() a task, but my local is. Local is on Commandbox, remote server is on Lucee… (some version). I’ll let you know more when I know more.

Nope. Well, yes, but not sure how. Turns out that somehow there were TWO instances of the scheduled task running. The one I was working with in my UI (and the only one I could find in the SchedulerService data) was indeed disabled. But there was another one, somewhere that was running. When I re-enabled the one I could see, my logs would show TWO sets of log entries. And just one when it was disabled. Something was lost somewhere, still running, that I couldn’t access. Unsure how it happened, but I know there were reinits in there. Might be worth looking into. Sorry I can’t tell you more about how it happened.

I’ll probably need to create some method of getting all the tasks, their current disabled states, then disable all of them, let the reinit proceed, then after initialization, re-enable those that weren’t disabled prior to the reinit. Obviously I’ll have to persist something to disk, and the entire idea sounds… fragile at best. At least now I can see what tasks are up and which aren’t. Maybe I should configure them ALL to be disabled by config, and upon any reinit I’ll just have to go re-enable them. Hmmm. Things to consider.