A durable job scheduling platform for dotnet
Check out the Sample project for a working example.
- Install the library
<ItemGroup>
<PackageReference Include="Jobba.Core" />
<PackageReference Include="Jobba.MassTransit" />
<PackageReference Include="Jobba.Redis" />
<PackageReference Include="Jobba.Store.Mongo" />
</ItemGroup>
or
dotnet add package Jobba.Core
dotnet add package Jobba.MassTransit
dotnet add package Jobba.Redis
dotnet add package Jobba.Store.Mongo
- Create a new
SampleJob
class that extendsAbstractJobBaseClass
. You can also create classes forJobState
andJobParameters
, or useobject
as a placeholder
using System;
using System.Threading;
using System.Threading.Tasks;
using Jobba.Core.Abstractions;
using Jobba.Core.Interfaces.Repositories;
using Jobba.Core.Models;
using Microsoft.Extensions.Logging;
namespace Jobba.Sample
{
public class SampleJobState
{
public int Tries { get; set; }
}
public class SampleJobParameters
{
public string Greeting { get; set; }
}
public class SampleJob : AbstractJobBaseClass<SampleJobParameters, SampleJobState> // or use `AbstractJobBaseClass<DefaultJobParams, DefaultJobState>` to not use state or parameters
{
private readonly ILogger<SampleJob> _logger;
public SampleJob(IJobProgressStore progressStore, ILogger<SampleJob> logger) : base(progressStore)
{
_logger = logger;
}
protected override async Task OnStartAsync(JobStartContext<SampleJobParameters, SampleJobState> jobStartContext, CancellationToken cancellationToken)
{
// implement your job's behavior
var tries = jobStartContext.JobState.Tries + 1;
_logger.LogInformation("Hey I'm trying! Tries: {Tries} {JobId} {Now}", tries, jobStartContext.JobId, DateTimeOffset.Now);
await LogProgressAsync(new SampleJobState { Tries = tries }, 50, jobStartContext.JobParameters.Greeting, cancellationToken);
await Task.Delay(100 * tries, cancellationToken);
if (tries < 10)
{
throw new Exception($"Haven't tried enough {tries}"); // jobba will retry if it encounters an exception
}
_logger.LogInformation("Now I'm done!");
}
public override string JobName => "Sample Job";
}
}
- In
Program.cs
, add the jobba configuration to theConfigureServices
callback inCreateHostBuilder
services
.AddLogging(o => o.AddSimpleConsole(c => c.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] "))
.AddJobba(jobba =>
jobba.UsingMassTransit() // use MassTransit as an event bus
.UsingMongo("mongodb://localhost:27017/jobba-sample", false) // Mongo currently is the only supported data store
.UsingLitRedis("localhost:6379,defaultDatabase=0") // Use LitRedis for distributed locking
.AddJob<SampleJob, SampleJobParameters, SampleJobState>() // `AddJob<SampleJob, object, object>` if not using state or parameters
)
.AddJobbaSampleMassTransit("rabbitmq://guest:guest@localhost/")
.AddHostedService<SampleHostedService>();
This example uses
- MassTransit as an event bus
- Mongo as a data store
- LitRedis for distributed locking
- Make a service
SampleHostedService
that extendsBackgroundService
and injects anIJobScheduler
using System;
using System.Threading;
using System.Threading.Tasks;
using Jobba.Core.Interfaces;
using Jobba.Core.Models;
using Microsoft.Extensions.Hosting;
namespace Jobba.Sample
{
public class SampleHostedService : BackgroundService
{
private readonly IJobScheduler _jobScheduler;
public SampleHostedService(IJobScheduler jobScheduler)
{
_jobScheduler = jobScheduler;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// create and schedule your job
var request = new JobRequest<SampleJobParameters, SampleJobState>
{
Description = "A Sample Job",
JobParameters = new SampleJobParameters { Greeting = "Hello" },
JobType = typeof(SampleJob),
InitialJobState = new SampleJobState { Tries = 0 },
JobWatchInterval = TimeSpan.FromSeconds(10),
MaxNumberOfTries = 100
};
await _jobScheduler.ScheduleJobAsync(request, stoppingToken);
}
}
}
Create a new JobRequest
and schedule it with _jobScheduler
in SampleHostedService
's ExecuteAsync
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// create a job request
var request = new JobRequest<SampleJobParameters, SampleJobState>
{
Description = "A Sample Job",
JobParameters = new SampleJobParameters { Greeting = "Hello" },
JobType = typeof(SampleJob), // the class for the job to run
InitialJobState = new SampleJobState { Tries = 0 },
JobWatchInterval = TimeSpan.FromSeconds(10), // time to wait between retries if the job fails
MaxNumberOfTries = 100 // maximum number of times to retry when a job fails
};
// schedule it with the passed-in cancellation token
await _jobScheduler.ScheduleJobAsync(request, stoppingToken);
}
Use the job's ID and the provided cancellation token to cancel a scheduled or running job
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// create a job request
var request = new JobRequest<SampleJobParameters, SampleJobState>
{
Description = "A Sample Job",
JobParameters = new SampleJobParameters { Greeting = "Hello" },
JobType = typeof(SampleJob), // the class for the job to run
InitialJobState = new SampleJobState { Tries = 0 },
JobWatchInterval = TimeSpan.FromSeconds(10), // time to wait between retries if the job fails
MaxNumberOfTries = 100 // maximum number of times to retry when a job fails
};
// schedule it with the passed-in cancellation token
await _jobScheduler.ScheduleJobAsync(request, stoppingToken);
// wait a second and cancel the job
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
await _jobScheduler.CancelJobAsync(request.Id, stoppingToken);
}