Ayende @ Rahien

It's a girl

Rotten Scheduling: Don’t roll your own

“We need to run a specific task every 72 hours, I thought about this approach…”

public class TimedTask
{
  public static Timer Timer;
  
  public static void Init()
  {
    Timer = new Timer(ExecuteEvery72Hours, null, TimeSpan.FromHours(72), TimeSpan.FromHours(72));
  }
  
  public static void ExecuteEvery72Hours()
  {
    // do something important
  }
}

This is a bloody rotten idea, let us see why…

  • What happens if your application is recycled every 29 hours?
  • What happens if your application is always on, but during that 72 hour call, it was offline?
  • What happens if your task actually takes more than 72 hours to run?
  • What happens if the task fails?
  • How do you report errors, warnings, etc?

Scheduling is a hard problem. There are a lot of things that you actually need to consider. And the code above is really considering none of them. I would be very surprised if something like that ever run. in production. It most certainly can’t be made to run reliably.

Things that run every X time, where X is a long time (hours / days) tend to be pretty important. In some of the systems that we wrote, that include doing things like updating VAT and interest rates, pulling from external source, generating the weekly report, etc.

You do not want this to be messed up.

If you need to do anything like that, make use of the builtin scheduling features of the OS you are running on (Windows Task Scheduler is an amazingly full featured, and cron isn’t bad if you are running on Linux). If you still insist on doing this in code, at the very least do something like this:

public class TimedTask
{
  public static Timer Timer;
  
  public static void Init()
  {
    Timer = new Timer(()=>
    {
      if( (DateTime.UtcNow - GetLastExecutedTime()) > 72)
        ExecuteEvery72Hours();
    }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
  }
  
  public static void ExecuteEvery72Hours()
  {
    // do something important
  }
}

This still has a lot of problems, but at least it it solving some crucial problems for you (note that GetLastExecutedTime has to be a persisted value).

Of course, if you need something like this in your code, you have better use something like Quartz, instead. Don’t roll your own. Sure, this is ten lines of code to do so, but as I said, this is the very basics, and it gets complex really fast.

Tags:

Posted By: Ayende Rahien

Published at

Originally posted at

Comments

Jonathan Gill
05/14/2012 09:18 AM by
Jonathan Gill

Why would you not use http://quartznet.sourceforge.net/ + topshelf

Andre
05/14/2012 09:30 AM by
Andre

Or use a scheduling framework like Quartz.NET (http://quartznet.sourceforge.net)

Steve Kennaird
05/14/2012 10:12 AM by
Steve Kennaird

We use Quartz.NET with Topshelf in production, found it to be very reliable. Add decent logging and email notifications on success/error and you can near enough "set and forget" it.

Wesley
05/14/2012 10:39 AM by
Wesley

I agree on using the builtin scheduling features of the OS. Writing code makes it more complex and a source for bugs and problems and more code to maintain.

Rafal
05/14/2012 10:53 AM by
Rafal

Quartz is an overkill for such a simple task - .Net gives you timers so all you need to do is to store job status & execution time somewhere... Don't make a pig of your application by adding unnecessary external dependencies, writing the execution time to a file is easier to do than learning quartz.net or topshelf.

Mads Topro
05/14/2012 11:03 AM by
Mads Topro

To schedule on Windows Task Scheduler need I create a simple console application to put it there?

Steve Kennaird
05/14/2012 11:16 AM by
Steve Kennaird

I think Ayende is talking about a one-off in a project so for simple tasks, sure, learning Quartz (and Topshelf) would be overkill. But when you start getting a collection of scheduled tasks, all running on different triggers, sharing logic and data layer etc, Quartz provides a decent framework and enforces patterns via IJob. Doesn't have to impact on the main app too much (or at all) if it's in its own solution whilst referencing only the projects it depends on from the main app's solution.

James Newton-King
05/14/2012 11:31 AM by
James Newton-King

Windows Task Scheduler + a console application is the way to go.

Simple, easy to debug and sys admins can go in and mess around with scheduling if they so desire.

John
05/14/2012 11:44 AM by
John

There are a number of Task Scheduler wrapper libraries you could use, such as http://taskscheduler.codeplex.com/

njy
05/14/2012 12:11 PM by
njy

+1 James for console + win scheduler (only on win, of course)

Moti
05/14/2012 12:23 PM by
Moti

@James The sys admin thing is the main reason why i stopped using windows task scheduler and moved to quartz.

My app needs something done every 72 hours? great. it is still a part of my app. Let me administer that myself (if needed, let the power user access to tasks admin page).

One of the worst things that can happen in production, is that a time sensitive task runs since some idiot decided to right click and ran it. Task scheduler won't even tell you who did it.

Gene Hughson
05/14/2012 03:26 PM by
Gene Hughson

Moti...that sounds more like a people problem than a technical problem. I see the ability to manually run something as a big win in the flexibility column.

Daniel
05/14/2012 04:24 PM by
Daniel

If I am alreading using SQL Server, does anyone have a reason to not use it as a scheduler?

http://msdn.microsoft.com/en-us/library/ms187880

Karep
05/14/2012 05:10 PM by
Karep

Thanks for letting me know about TopShelf guys!

Karep
05/14/2012 05:13 PM by
Karep

@Daniel: If you run SQL jobs then ok.

Ken Egozi
05/14/2012 05:37 PM by
Ken Egozi

@nyt - win only? *nix has crond

OS schedulers are pretty darn good.

Rafal
05/14/2012 07:03 PM by
Rafal

Oh come on, unix has console too but it's lame compared to windows cmd.exe

njy
05/14/2012 09:33 PM by
njy

@Ken: i was referring to task scheduler specifically, that's only on windows. on *nix there's cron, i agree

Moti
05/15/2012 12:07 PM by
Moti

@Gene

I do provide the ability to run the quartz tasks manually, I just do it from my own interface and application, under my own security model, under my own business logic (some tasks I won't allow running during work hours, unless you mark the application as 'down for repairs'), under my scalability model - basically, under my terms.

I want full control over my app. I can get it with task scheduler as well, but i have to work twice as hard.

Comments have been closed on this topic.