Reliable job scheduling with NServiceBus and Quartz.NET

One of the more interesting features of NServiceBus is the ability to schedule messages and send messages in the future. The default implementation works well for simple cases, where you have messages that need to be sent every X seconds. But for many of our scenarios, we are more calendar based.

Instead of something every 5 seconds, we wanted to do “Every second Tuesday” or “Daily at 6 AM” or “First of the month”. These more complicated schedules are easy to build with tools like Windows Task Scheduler or cron. Those tools work great, but we had problems with them in our production environments:

  • When a job failed, we didn’t get notified
  • It forced us to create console apps for everything
  • Logging left something to be desired
  • Is not .NET-based

We started gravitating towards an architecture where our “jobs” were really just NServiceBus handlers initiated by messages. This gave us all the durability, logging, retry etc etc that WTS doesn’t have. Plus, it was more efficient for us to just keep the job processes in memory, instead of processes waking up to do something then going to sleep. In our new architecture, we created a job initiator and a series of job executors:


We then used Quartz.NET as a 100% C#-based job scheduler for initiating jobs. Both of these boxes are NServiceBus hosts, but the Job Initiator is a send-only endpoint. The job runners are really just NServiceBus hosts with handlers to execute the jobs. Job runners are decoupled from the schedule they run, and the job initiator is decoupled from what work actually happens as part of a job.

Integrating NServiceBus and Quartz.NET is fairly straightforward, but is a little bit of code. I’ve put the full example up on my GitHub that you can download and run (readme details how).

But basically, I got it to the point where I have my job initiator and definition of a job:

public class DoSomethingJob : IJob
    public IBus Bus { get; set; }

    public void Execute(IJobExecutionContext context)
        Bus.Send(new DoSomething());

Where I just send a message as part of my job. Then I can define the schedule to initiate that job:

public class DoSomethingSchedule : ScheduleSetup<DoSomethingJob> 
    public DoSomethingSchedule(IScheduler scheduler) 
        : base(scheduler)

    protected override TriggerBuilder CreateTrigger()
        return TriggerBuilder.Create()
            .WithSchedule(CronScheduleBuilder.MonthlyOnDayAndHourAndMinute(1, 5, 0));

This schedule runs just once a month, on the first of the month, at 5 AM. In the sample, I also configure Quartz to use durable, database-backed schedules to be able to handle service stoppage/restarts.

But why go this route instead of just normal cron/task scheduler jobs? For me, I wanted to have more reliability in my scheduled jobs, and moving to NServiceBus as the executor of those jobs gave me that. Quartz.NET then gave me the ability to host scheduling 100% in .NET, whereas the Task Scheduler API is….interesting to say the least.

It’s not for everyone, but in our systems where we already have NServiceBus in use, adding Quartz.NET let us easily use messaging for job execution.

Download Quartz.NET NServiceBus Sample

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in NServiceBus. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Joseph Daigle

    We’ve used this exact setup in production for some time now (over a year). It works perfectly and is really easy to troubleshoot and manage.

  • Maksim

    Have anyone considered using NServiceBus “timeout
    manager” alone for that? In that case, for every job we would have a handler that decides whether
    job should be executed or not (in case of server restart/failover), defers message for next execution and executes job itself.

    I believe it is also a little bit of code, but
    instead we could remove unnecessary dependencies.

    • Anonymous

      That’s basically what the built-in scheduler in NServiceBus does. It works really well for interval-based ones that use a time span to determine cadence of firing.

      But not so hot for specific schedule-based ones, where it’s sensitive to run on explicit dates/times. I’ve gotten the push back before on not taking on a 3rd-party tool for scheduling, but I guess I just prefer not to build infrastructure components myself.

  • RichB

    > It forced us to create console apps for everything

    Not true. You could have used a cscript script to call into a ServicedComponent via COM+. Then you get the lovely benefits of strong naming. Really.

    • Anonymous

      Ha wow. Maybe we can throw in calling a sproc and have it call out to COM too?

  • Mike Mozhaev

    How is “job failed to start” handled?

    • Anonymous

      There’s two things here – one is, did the Quartz job not fire to send the original message. In that case, we audit jobs from Quartz to make sure they get fired w/o errors.

      In the handler side, because we’re using NServiceBus, if the handler fails, the message retries. If it fails after that, the message drops into an error queue, which we also monitor. Does that make sense?

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1167()

  • Pingback: Distributed Weekly 168 — Scott Banwart's Blog()

  • Swab Jat

    Try a commercial solution – it even go as far as load balancing and user interface to recall computation result.

  • Matt

    Hi there! Stumbled across your post looking for options to take care of job scheduling in my environment. I very much was interested in the approach you took, and was wondering now that a significant amount of time has passed if you ran into anything that you’d change if you had to do it over again?

    Did you wrap your jobs in any kind of common “success verification” layer or did the service queuing mechanism take care of that for you?

    Obviously the machine running the scheduler can be very lightweight, after all there isn’t much processing going on – was there any heuristic analysis of your logs helping keep track of failed jobs?