Horarium is an open source job scheduling .NET library with an easy to use API, that can be integrated within applications of any scale - from the smallest stand-alone application to the largest e-commerce system.
Horarium is fully based on an asynchronous work model, it allows you to run hundreds of parallel jobs within a single application instance. It supports jobs execution in distributed systems and uses MongoDB as a synchronization backend.
Horarium supports .NET Core/netstandard 2.0 and .NET Framework 4.6.2 and later.
Support Databases
Database | Support |
---|---|
MongoDB | Yes |
In Memory | Yes |
PostgreSQL | Not yet #6 |
Add nuget-package Horarium
dotnet add package Horarium
dotnet add package Horarium.Mongo
Add job that implements interface IJob<T>
public class TestJob : IJob<int>
{
public async Task Execute(int param)
{
Console.WriteLine(param);
await Task.Run(() => { });
}
}
Create HorariumServer
and schedule TestJob
var horarium = new HorariumServer(new InMemoryRepository());
horarium.Start();
await horarium.Create<TestJob, int>(666)
.Schedule();
Add nuget-package Horarium.AspNetCore
dotnet add package Horarium.AspNetCore
Add Horarium Server
. This regiters Horarium as a hosted service, so .Net core runtime automatically starts and gracefully stops Horarium.
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddHorariumServer(MongoRepositoryFactory.Create("mongodb://localhost:27017/horarium"));
//...
}
Inject interface IHorarium
into Controller
private readonly IHorarium _horarium;
public HomeController(IHorarium horarium)
{
_horarium = horarium;
}
[Route("api")]
public class HomeController : Controller
{
[HttpPost]
public async Task Run(int count)
{
await _horarium.Create<TestJob, int>(count)
.Schedule();
}
}
Add job that implements interface IJobRecurrent
public class TestRecurrentJob : IJobRecurrent
{
public Task Execute()
{
Console.WriteLine("Run -" + DateTime.Now);
return Task.CompletedTask;
}
}
Schedule TestRecurrentJob
to run every 15 seconds
await horarium.CreateRecurrent<TestRecurrentJob>(Cron.SecondInterval(15))
.Schedule();
Sometimes you need to create sequence of jobs, where every next job would run if and only if previous job succeeds. If any job of the sequence fails next jobs won't run
await horarium
.Create<TestJob, int>(1) // 1-st job
.Next<TestJob, int>(2) // 2-nd job
.Next<TestJob, int>(3) // 3-rd job
.Schedule();
Horarium has two types of workers: server and client. Server can run jobs and schedule new jobs, while client can only schedule new jobs.
Horarium guarantees that a job would run exactly once
Every Horarium instance consults MongoDB about new jobs to run every 100ms (default), thus creating some load on the DB server. This interval can be changed in HorariumSettings
If you want to decrease load, you can use job throttling that will automatically increase interval if there are no jobs available after certain attempts. To enable this feature, pass JobThrottleSettings
to HorariumSettings
with property UseJobThrottle
set to true
.
var settings = new HorariumSettings
{
JobThrottleSettings = new JobThrottleSettings
{
UseJobThrottle = true
}
};
For more information about configuration, see JobThrottleSettings
To use Horarium with SimpleInjector one should implement its own IJobFactory
, using Container
from SimpleInjector
. For example:
public class SimpleInjectorJobScopeFactory : IJobScopeFactory
{
private readonly Container _container;
public SimpleInjectorJobScopeFactory(Container container)
{
_container = container;
}
public IJobScope Create()
{
var scope = AsyncScopedLifestyle.BeginScope(_container);
return new SimpleInjectorJobScope(scope);
}
}
public class SimpleInjectorJobScope : IJobScope
{
private readonly Scope _scope;
public SimpleInjectorJobScope(Scope scope)
{
_scope = scope;
}
public object CreateJob(Type type)
{
return _scope.GetInstance(type);
}
public void Dispose()
{
_scope.Dispose();
}
}
Then add HorariumServer
(or HorariumClient
):
container.RegisterSingleton<IHorarium>(() =>
{
var settings = new HorariumSettings
{
JobScopeFactory = new SimpleInjectorJobScopeFactory(container),
Logger = new YourHorariumLogger()
};
return new HorariumServer(jobRepository, settings);
});
In case of HorariumServer
, don't forget to start it in your entypoint:
((HorariumServer) container.GetInstance<IHorarium>()).Start();
When a job fails, Horarium can handle this exception with the same strategy.
By default, the job repeats 10 times with delays of 10 minutes, 20 minutes, 30 minutes and etc.
You can override this strategy using IFailedRepeatStrategy
interface.
Example of default DefaultRepeatStrategy
implementation:
public class DefaultRepeatStrategy :IFailedRepeatStrategy
{
public TimeSpan GetNextStartInterval(int countStarted)
{
const int increaseRepeat = 10;
return TimeSpan.FromMinutes(increaseRepeat * countStarted);
}
}
This class is called every time when a job fails, and it has to return TimeSpan
of the next scheduled job run.
To override default behavior globally, change settings in HorariumSettings
new HorariumSettings
{
FailedRepeatStrategy = new CustomFailedRepeatStrategy(),
MaxRepeatCount = 7
});
To override the default behavior for a particular job:
await horarium.Create<TestJob, int>(666)
.MaxRepeatCount(5)
.AddRepeatStrategy<DefaultRepeatStrategy>()
.Schedule();
If you want to disable all repeats, just set MaxRepeatCount
to 1
new HorariumSettings
{
MaxRepeatCount = 1
});