Saturday, August 27, 2022

Replacement Task Scheduler for Sitecore

In this article we discuss some of the challenges with the out-of-the-box Task Scheduler included with Sitecore and see how you can replace it with Hangfire, a product to perform background processing for .Net applications.

Update: A NuGet package is available for download here.

There are ton of articles describing the Task Scheduler and oftentimes cover the same information. Below are few to get you started:

Oddly the only things I can find on the Sitecore docs site is from the old SDN. I'll skip linking that here because it is likely to break.

Some of the issues you'll find with the Task Scheduler is the inability to run at a specific time and if Sitecore shuts down the missed tasks are likely to run immediately following startup. With the use of Hangfire we'll address both issues. The format of the schedule field is also a bit crazy and so we'll add to the complexity by including support for the cron format.

So why not SiteCron? You should use it. If however you can't use it, don't want to use it, or simply can't make up your mind then feel free to give this a try.

Here is a quick breakdown of what we'll build:

  1. Pipeline processor inheriting from Sitecore.Owin.Pipelines.Initialize.InitializeProcessor which should give us access to IAppBuilder. Here we'll register Hangfire and handle scheduling of jobs.
  2. Configuration patch to disable the agent used for scheduled tasks and to register our new processor. There are multiple agents used for scheduled tasks, so at the moment we'll only focus on the one that runs for master in the Standalone and ContentManagement roles.

The important part

Sitecore configuration patch to register the code:

  • Register processor in the own.initialize pipeline. The first option allows you to provide the amount of time to delay running scheduled tasks after Sitecore starts up; this is extremely helpful if you want to disable jobs and need some extra time or the jobs are process-intensive and you want to give Sitecore time to warm up. The second option allows you to adjust the frequency in which schedules configured in Sitecore (from the Content Editor) are updated in Hangfire; this is important for cases where Admins create/update/remove scheduled tasks.
  • Disable the the Master_Database_Agent so the old scheduler doesn't run.
Sitecore processor:

  • You'll need references to Hangfire.AspNetHangfire.Core, and Hangfire.MemoryStorage found on Nuget. I went with in-memory storage because I like things simple and having to setup connections to a database and custom tables sounds like a pain. Also, I feel like looking at the LastRun field was enough to satisfy what I needed.
  • In the Process method we do the basic configuration for Hangfire. I don't care about the dashboard like described here and here. Once again, I like things simple and adding another thing to maintain is just not for me. If you want to then go knock yourself out. We also schedule a fire-and-forget background job to initialize the scheduler which includes registering Scheduled Tasks defined in Sitecore.
  • Once the first background job runs, which may execute tasks that missed their scheduled run time, the recurring jobs are configured. Any scheduled task with an empty/invalid schedule is ignored. The creation of the recurring jobs are chained to the Initialize method so we can ensure that anything missed runs before we schedule others. This may be a problem if you have really long running jobs.
  • The default schedule format {start timestamp}|{end timestamp}|{days to run bit pattern}|{interval} is compatible but converted to a cron schedule format when registered (see GetSchedule method). The interval may be converted to a precise time when using the daily TimeSpan format. For example, the value 1.04:30:00 will run daily at 4:30 AM. The conversion code produces a cron format 30 4 * * * which is what ultimately gets provided to Hangfire. If you want to replace the default schedule format with a cron schedule format go right ahead as both formats are supported. The only problem I saw with this is when using the SPE Task Manager because the tool doesn't know anything about cron.
A simple report can be written with Sitecore PowerShell Extensions to display the current configured recurring jobs.

We've had it running for quite some time now and it has been a game changer for us. Think of all the scheduled tasks that seem to randomly run during deployments or competing with processing power needed by Content Editors.

Give it a try and let me know how it works out for you on your projects. Feedback welcome.

More Hangfire:

Disclaimer: After sharing this post I was reminded of an important distinction one should make with bolting on more features to the Sitecore platform. I found Hangfire helped solve an issue we were having with our scheduled tasks. Every day we run tasks that execute SPE scripts and these can be quite CPU intensive. The data belongs in Sitecore and the tasks need to be run outside of business hours (late night or early morning). As a separate application we built a sync with Quartznet using dotnet 6 (latest LTS version at the time) which dramatically improved the developer experience, performance, and maintainability.