How do I delete a trigger for a job in Quartz.net and keep the job? This is only an issue when deleting the last trigger on the job, right now it deletes the job as well.
The code I am using is:
_scheduler.UnscheduleJob(trigger.Key);
and that works fine as long as the job for that triggers has more than one trigger. If this is the last trigger the job is also deleted, and that is something I don't want.
When you create your job you have to specify that you want it to stick around after all triggers have been deleted, which you do by calling StoreDurably()
eg
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "group1")
.StoreDurably()
.Build();
You can also use
// Unschedule a particular trigger from the job (a job may have more than one trigger)
scheduler.UnscheduleJob(new TriggerKey("trigger1", "group1"));
Note that my syntax is slightly different than the Quartz.net reference
Related
I have a method which can be called many times
public void DoSomething(){
IJobDetail job = JobBuilder
.Create<NotificationResendJob>()
.UsingJobData(newJobDataMap)
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("HelloWorldJob-trigger")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
.Build();
var scheduler = await _schedulerFactory.GetScheduler();
await scheduler.ScheduleJob(job, trigger);
}
It works fine, but if it is invoked more than once, then an exception is thrown because there is an instance already running.
My question is, it is possible to create a method that will build a new job and trigger for every method invocation, supposing that this method will not be called often
For example user clicks a button and a job is triggered, another user after some time clicks this button too, and another job is triggered for him
Unable to store Trigger: 'DEFAULT.HelloWorldJob-trigger', because one already exists with this identification.'
You probably want to make a distinction between job and a trigger. Usually job is a logic that can be run multiple times and even multiple parameters.
Say job is GreetPerson, so you want to run this every morning, so once a day, but also for different people.
You can define an IJob implementation that does the Console.Writeline using data from job data map. Job data map consists of values from both Job (more global) and trigger (per invocation, there can be multiple different triggers).
In this case you could call
string personToGreet = context.User.DisplayName;
JobDataMap dataMap = BuildJobDataMapForThisInvocation(personToGreet);
IScheduler.TriggerJob(theJobKey, myJobParameters);
Then the job would retrieve invocation (trigger) specific data from map and would be reusable among different use cases.
I have a hopefully a simple question to which i can't find the answer.
If i schedule a Job of type LongRunningAndHeavyJob on demand (StartNow()) i dont know how long this job will run but it can be long (from 10 minutes to 60+ minutes) and is very CPU intensive. Because the end-users can start this job on demand i want to make sure only one instance of this job is running at a certain time.
So for example:
John click on button schedule at 14:00:00
Quartz.net server will run this job
Jane clicks on the button schedule at 14:05:00 but the job of John is still running
How can i make sure that the job of Jane will not run at the same time with the job of John and will run after the job of John is finished.
The attribute DisallowConcurrentExecution is only for use if you have a job that is schedule with a trigger that is repeating i think and the job need to have the same key which i don't have.
Some extra info:
Job of John has different JobData than the job of Jane
Thanks in advance
I fixed the problem that i was facing!
How did i do it:
Decorated the Long running jobs with the [DisallowConcurrentExecution] attribute
When the Quartz server is starting i add the long running jobs as Durable jobs without any trigger.
After someone requests the job to run i will set the JobData on the trigger instead of the job and schedule the job by only giving the trigger to the ScheduleJob method (trigger uses ForJob(IJobDetail) method)
This will make sure only 1 instance of the Job will be running at a given time and if someone else triggers the same job it will be scheduled to run after the first one is completed and there are thread in the threadpool available
Server part:
private void RegisterManualJobs() {
var createTournamentScheduleJob = JobBuilder.Create<CreateTournamentScheduleJob>().WithIdentity("CreateTournamentSchedule", JobGroups.JG_TournamentScheduleJobs).StoreDurably().Build();
var createTournamentScheduleSingleEventJob = JobBuilder.Create<CreateTournamentScheduleSingleEventJob>().WithIdentity("CreateTournamentScheduleSingleEvent", JobGroups.JG_TournamentScheduleJobs).StoreDurably().Build();
Scheduler.AddJob(createTournamentScheduleJob,true);
Scheduler.AddJob(createTournamentScheduleSingleEventJob,true);
}
Client part:
var job = Scheduler.GetJobByName("CreateTournamentSchedule", JobGroups.JG_TournamentScheduleJobs);
var trigger = TriggerBuilder.Create().StartNow().WithIdentity($"Trigger-CreateTournamentSchedule-{TournamentID}", JobGroups.JG_TournamentScheduleJobs).UsingJobData(data).ForJob(job).Build();
Scheduler.ScheduleJob(trigger);
The GetJobByName() is one of the extensionmethods i wrote on the IScheduler
Is there a way to tell Quartz.NET not to fire two triggers at the same time? That means if trigger A and trigger B have the exact same schedule, then trigger B waits a certain amount of time and then fire?
I have seen in my program that this can cause an issue when both my jobs are reading from the same file and executing the same .exe file. This causes an uncaught exception that I am yet to figure out.
I am not sure how Quartz.NET handles this. But is there a way to delay such triggers (even if it's just for a few seconds)?
You can use DisallowConcurrentExecutionAttribute for your job.
[DisallowConcurrentExecutionAttribute]
class DisallowConcurrentJob : IJob
{
//Implementation goes here
}
It prevents multiple instances of a job WITH THE SAME KEY from running
at the same time.
A very good explanation can be found here.
UPDATE:
If you want to be sure that the trigger/job always runs you can use misfire instructions:
IJobDetail job1 = JobBuilder.Create<InheritedJob1>()
.WithIdentity("DisallowConcurrentJob", "MYGROUP")
.RequestRecovery(true)
.Build();
//Schedule this job to execute every second, a maximum of 5 times
ITrigger trigger1 = TriggerBuilder.Create()
.WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForTotalCount(5)
.WithMisfireHandlingInstructionFireNow())
.StartNow()
.WithIdentity("DisallowConcurrentJobTrigger", "MYGROUP")
.Build();
Scheduler.ScheduleJob(job1, trigger1);
WithMisfireHandlingInstructionFireNow
The job is executed immediately after the scheduler discovers misfire situation.
I am very new to using Quartz and I have a question regarding triggers. Is it possible to trigger based on file existence? I would like to have Quartz run a job until a certain file is found, then stop running that job and perhaps move on to a different one.
For example, I would like to do something like this:
(1) Job1 checks if File.txt exists in a given directory every 60 seconds.
(2) If File.txt is found, trigger Job2 to start. Job1 stops checking for file existence.
Right now, I have:
// Job definitions
var Job1 = JobBuilder.Create<TestEmail>().WithIdentity("job1", "group1").Build();
var Job2 = JobBuilder.Create<TestFileTrigger>().WithIdentity("job2", "group2").Build();
// Triggers
ITrigger trigger1 = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1").StartNow()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
.Build();
ITrigger trigger2 = TriggerBuilder.Create()
.WithIdentity("trigger2", "group2").StartNow()
.Build();
// Schedule jobs
scheduler.ScheduleJob(Job1, trigger1);
if (TestFileTrigger.fileExistence == true)
{
scheduler.ScheduleJob(Job2, trigger2);
}
but it seems like Job2 never starts.
TestEmail and TestFileTrigger simply print to console at the moment. The boolean TestFileTrigger.fileExistence comes from checking if a file exists at a given location (which it does).
Edit:
TestFileTrigger.fileExistence is a boolean. Added definitions of Job1/Job2 if that helps.
Edit:
I found that if I put Thread.Sleep(TimeSpan.FromSeconds(x)); before the if statement, the if statement will run if the condition is met. (Where x is some number of seconds.) Why does it work in this case, but not otherwise? I cannot always know how many seconds it will take for the condition to be met.
What type of application is this?
If this is, for example, a Windows service - to keep the scheduler alive so that it hangs around to execute the jobs according to your triggers, you need to do something like:
ThreadStart start = SetupSchedules()
var thread = new Thread(start) { Name = "mysvc" }
thread.Start();
.. this would go into the override void OnStart(string[] args) method of the Windows service.
The SetupSchedules method would be the thing that hooks into Quartz jobs and would be something like (The code you've written above in the OP would make a good start):
ISchedulerFactory factory = new StdSchedulerFactory();
JobScheduler = factory.GetScheduler();
JobScheduler.ScheduleJob(job1, trigger1);
This should keep it alive so that it executes the jobs. I've omitted a bunch of stuff here, but hopefully this should give you a few pointers to help weave it into your app.
You will also need something like this:
private void ManageThread()
{
var _thread = Thread.CurrentThread;
while (!_threadMustStop) // false by default, set this to true in a 'shutdown' process
{
Thread.Sleep(10000);
}
}
...which you call from your SetupSchedules method
It looks like you don't understand concurrency and threading that is involved here.
The issue is as follows.
Your MAIN thread does the following.
Create two jobs
Give each job a trigger
Schedule Job1 to start
Check TestFileTrigger.fileExistence and if true, start Job2 (it is false so it doesn't run).
THEN a threadpool thread will start Job1. Most likely setting TestFileTrigger.fileExistence = true. But Main thread has already completed it work.
FIN.
At no point do you go back and check if TestFileTrigger.fileExistence is true. So its result is irrelevant. You are in fact checking the result BEFORE you get a result.
By adding a Thread.Sleep you give the job1 enough time to complete and give you a result (job1 runs asynchronously and concurrently, and it is clear you expected it to run synchronously). Imagine for example you tell your friend Fred to go to the shop to buy Pizza and place it on your desk (asynchronous), then turn around straight away and wonder why there is no pizza on your desk.
Synchronous would be if you yourself went to the shop, bought a pizza and took it home and placed it on your desk, THEN eating pizza from your desk.
JobScheduler.ScheduleJob(job1, trigger1); does work asynchronously.
You should create a job to wrap up step 4 and schedule that to run periodically, OR you use the built in FileScanJob instead.
I successfully set up a two node scheduler cluster to execute a job. I used OracleODP-20 connector to connect to oracle database, where the QRTZ_ tables are created. Job scheduled is also getting executed by either one of the scheduler in every 5 minutes.
This is my trigger:
ISimpleTrigger trigger = (ISimpleTrigger) TriggerBuilder.Create()
.WithIdentity("mytrigger", schedId)
.StartNow()
.WithSimpleSchedule(x => x.WithRepeatCount(-1).WithInterval(TimeSpan.FromMinutes(5)))
.Build();
But there is one problem.When the scheduler is starting up, it always executes
the job, even if time for the next execution of the job is not now.
e.g.,
Job1 will run every 5 minutes.
Scheduler1 started at 10.00 and it
executes the Job1 and is waiting for 10.05 for the next execution.
When i start Scheduler2 at 10.01, even though Job1 was supposed to
run next 10.05, it will run at 10.01
After this initial run, the Job1 will be executed by only one of the scheduler and it goes fine.
But I am not sure how to tell
the Scheduler2 that, the job is already executed by Scheduler1 and don't execute it till the next fire time.
Any help in this?
You should create this trigger only once, not on each node's startup.
I see that you trigger contains instance's id as group which is very suspicious. It seems that you are having trigger for each node when you should have just single trigger created if it does not exist yet.
Easier to read trigger definition would thus be:
ISchedulerFactory sf = new StdSchedulerFactory(properties);
IScheduler sched = sf.GetScheduler();
var jobDetail = JobBuilder.Create<NoOpJob>().Build();
var key = new TriggerKey("trigger-name", "trigger-group");
if (sched.GetTrigger(key) == null)
{
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(key)
.StartAt(DateBuilder.EvenHourDate(DateTimeOffset.UtcNow))
.WithSimpleSchedule(x => x
.RepeatForever()
.WithInterval(TimeSpan.FromMinutes(5)))
.Build();
sched.ScheduleJob(jobDetail, trigger);
}
I threw in the even hour date to get more precise timings instead of 'from now every 5 minutes'. You could easily get this with cron trigger too.