Quartz.net CancellationToken
Asked Answered
S

2

13

In my scheduler, implemented with quartz.net v3, i'm trying to test the behaviour of the cancellation token:

....
IScheduler scheduler = await factory.GetScheduler();
....
var tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
// Start scheduler
await scheduler.Start(ct);
// some sleep 
await Task.Delay(TimeSpan.FromSeconds(60));
// communicate cancellation
tokenSource.Cancel();

I have a test Job that runs infinitely and in the Execute method checks the cancellation token:

public async Task Execute(IJobExecutionContext context)
{
    while (true)
    {
        if (context.CancellationToken.IsCancellationRequested)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
        }
    }
}

I would expect that when tokenSource.Cancel() is fired the job will enter in the if and throws the Exception. But it doesn't work.

Shrove answered 24/1, 2018 at 17:14 Comment(0)
S
12

According to the documentation, you should use Interrupt method to cancel Quartz jobs.

NameValueCollection props = new NameValueCollection
{
    { "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
var scheduler = await factory.GetScheduler();
await scheduler.Start();
IJobDetail job = JobBuilder.Create<HelloJob>()
    .WithIdentity("myJob", "group1")
    .Build();
ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("myTrigger", "group1")
    .StartNow()
    .WithSimpleSchedule(x => x
        .WithRepeatCount(1)
        .WithIntervalInSeconds(40))
    .Build();
await scheduler.ScheduleJob(job, trigger);
//Configure the cancellation of the schedule job with jobkey
await Task.Delay(TimeSpan.FromSeconds(1));
await scheduler.Interrupt(job.Key);

Scheduled job class;

public class HelloJob : IJob
{
    public async Task Execute(IJobExecutionContext context)
    {
        while (true)
        {
            if (context.CancellationToken.IsCancellationRequested)
            {
                context.CancellationToken.ThrowIfCancellationRequested(); 
                // After interrupt the job, the cancellation request activated
            }
        }
    }
}

Apply scheduler.Interrupt after the job executed and the quartz will terminate the job.

EDIT

According to source code (Line 2151), the Interrupt method applys cancellation tokens of the job execution contexts. So, it could be better to use facility of the library.

Seleucid answered 24/1, 2018 at 18:9 Comment(5)
Ok, but said so, what's the point of the CancellationToken in the parameters in the Start() and in the Shutdown() methods of the Scheduler? I can't understand ...Shrove
I couldn't find any documentation about it.Seleucid
Anyway, I've implemented an example with your code. It seems not to work. Indeed if you put an await Task.Delay(TimeSpan.FromSeconds(n)) after the tokenSource.Cancel() the job still run till the end of the n seconds.Shrove
This works. But i still don't understand how to use the CancellationToken in the methods parameters. Now even in the .Interrupt()Shrove
@Shrove - Late response but perhaps if someone also encounters this thread - CancellationToken in Start, Interrupt and other Quartz methods are to cancel that particular operation - e.g. cancel starting scheduler or cancel interrupting a job. This can be handy when using Ado job store and somethings goes wrong with db connection and it's just waiting and waiting, but you need to shutdown gracefully your app, and here it comes the cancellation token - you can cancel that operation and continue shutdowning your app.Corroborant
K
2

Here is a Unit Test from Github Repo: https://github.com/quartznet/quartznet/blob/master/src/Quartz.Tests.Unit/InterrubtableJobTest.cs

I tried to implement the cancellation the same way, but it didn't work for me either.

@Stormcloak I have to check the cancellation request because I want to do some aborting operations for the job, e.g. write status data to a database.

EDIT:

So, after multiple tests and implementations. I've got it running.

Some Pseudo code here:

    this.scheduler = await StdSchedulerFactory.GetDefaultScheduler();
    this.tokenSource = new CancellationTokenSource();
    this.token = tokenSource.Token;
    // Start scheduler.
    await this.scheduler.Start(token);
    // add some jobs here
    // ...
    // cancel running jobs.
    IReadOnlyCollection<IJobExecutionContext> jobs = await this.scheduler.GetCurrentlyExecutingJobs();
    foreach (IJobExecutionContext context in jobs)
    {
       result = await this.scheduler.Interrupt(context.JobDetail.Key, this.token);
    }
    await this.scheduler.Shutdown(true);

So now you can use the CancellationToken in your Execute method.

Kleptomania answered 25/1, 2018 at 11:32 Comment(2)
The thing that I don't understand like I said in the comment to @Stormcloak's answer is how to use the CancellationToken parameter in the .Interrupt method. 'Cause it seems that if you pass it or not to the method the result still the same.Shrove
@Shrove That's because the token that is pass to the interrupt is to cancel the interrupt function not the Job. Interrupt will signal the Job for cancellation and if it is a long running job it will cancel the next time the context.CancellationToken.ThrowIfCancellationRequested(); is evaluated.Limoges

© 2022 - 2024 — McMap. All rights reserved.