Turnerj PageViews += 1;

Halt and Hangfire

Apr 9, 2019

I have a website and I want to schedule a task to run every X minutes. Majority of the time, you would reach for Cron, throw together a fancy CRON expression and you would be on your way.

This is great for a world where you deploy to a Linux server but isn't how you approach scheduling tasks on Windows. In Windows, you have the Task Scheduler and while it is powerful, I have always found it a bit cumbersome. In any case, using either Cron or Task Scheduler, you have achieved your goal of having a scheduled task run.

Now, like many projects, requirements change and you not only need a scheduled task but adhoc background tasks too. That's fine, you can work out some way of instantiating another "instance" of your code in the background to do work. It might be something dodgy like a website doing a request to itself and not waiting for a response or something as simple as running a command on the shell. Again, your problems are "solved".

Another change to requirements comes around (third times the charm, am I right?) and now you need a dashboard to view and manage these tasks...

I can't speak to how you would achieve this in other frameworks however if you are using .NET, you are in luck thanks to Hangfire by Sergey Odinokov.

What's Hangfire?

Hangfire is a library that allows you to have both scheduled and adhoc background tasks in your application, backed by persistent storage. These tasks can then be viewed through the Hangfire dashboard to see what is running, what will run and what has failed.

Hangfire supports automatic retrying of tasks and can link into both your error logging and dependency injection systems making it easy to connect to your application.

One of my favourite features of Hangfire however is that due to how it is built, it automatically supports distributed tasks across multiple servers. With this in mind, it is good to have many small tasks to make the most use of this.

It isn't limited to ASP.NET applications either, you can have your Hangfire server be a Windows Service for all it cares!

Hangfire is free for personal and commercial use however has premium features like batching/grouping tasks together.

Example

Before anything else, you will need to add Hangfire from Nuget.

For an ASP.NET Core application, you would need to update your ConfigureServices method in your Startup class to include the following:

public void ConfigureServices(IServiceCollection services)
{
	// ...

	services.AddHangfire(c =>
	{
		c.UseSqlServerStorage("YOUR_CONNECTION_STRING");
	});

	// ...
}

While my example is using SQL Server, Hangfire supports various other storage systems including MongoDB or Redis.

Now in the Configure method of your Startup class, you need to actually trigger the server and dashboard (though dashboard is optional).

app.UseHangfireDashboard("/hangfire");
app.UseHangfireServer();

Now when your application begins, the Hangfire server will start and be able to process tasks. You will also be able to view the Hangfire dashboard at the path you specified on UseHangfireDashboard.

So that gets the server start, what about the tasks that are meant to run in the background?

Scheduled Tasks

In your Configure method in your Startup class, you will want to add something like the following for a scheduled task:

RecurringJob.AddOrUpdate(() => Console.Write("Look ma, a recurring task!"), "0 * * * *");

While that is a simple example, you do need to keep your background tasks simple because the expression is serialised. With how I use Hangfire for scheduled tasks, it looks more like:

RecurringJob.AddOrUpdate<MyBackgroundTaskClass>(instance => instance.RunTask("Whatever", "arguments", 1, "like"), "MY CRON EXPRESSION")

This would create an instance of MyBackgroundTaskClass (with DI support) and call the RunTask method with the specified arguments. You can pass various arguments to the tasks however for compatibility and simplicity, it is best to provide only essential arguments in the simplest form. This is because the data is serialised and some types serialise easier than others. Personally, I would pass IDs referencing items in the DB that the task should then query itself.

In that example, the class MyBackgroundTaskClass nor the RunTask method need to be anything special. They don't need special attributes or does the class need to inherit from a specific class or interface.

Adhoc Tasks

For adhoc tasks anywhere in your application, it is just as simple:

BackgroundJob.Enqueue<MyBackgroundTaskClass>(instance => instance.RunTask("Whatever", "arguments", 1, "like"))

Same rules apply like the recurring job - you will want to keep the arguments simple rather than passing through complex objects.

Summary

Hangfire provides a simple way for you to manage background tasks, scheduled and adhoc, for your .NET application. I'm only scratching the surface in this post, there are many more nuanced pieces of functionality that make Hangfire great.

I highly recommend going through the documentation for a more in-depth dive into Hangfire.

Hope you enjoyed reading this and will give Hangfire a look in your next project!