Unpausing Quartz trigger causes many job executions - c#

I have a job that is scheduled to run every 30 seconds with a Quartz Crontrigger. The schedule is thus "0/30 * * ? * MON-FRI". It has no problem running.
However, I want to pause the job for one minute. So I call scheduler.PauseAll();, which is supposed to pause all triggers in the scheduler. I then have the thread sleep for one minute and then call scheduler.ResumeAll();. So my code looks something like this:
scheduler.PauseAll();
System.Threading.Thread.Sleep(60000);
scheduler.ResumeAll();
The trigger pauses for the appropriate time, but when it resumes, the job will run twice. I have the jobs set with the misfire handling .WithMisfireHandlingInstructionDoNothing(), so I don't understand why the job executes twice in this case. I read this question, but the OP there paused the job instead of the triggers, so I didn't find it helpful.

Related

System.Threading.Timer does not work correctly

I notice the timer is not correct.
This is a very simple C# code: it will print current date/time every 1 minute.
My expected result is: let it run at 3:30 PM then we will have: 3:31 PM, 3:32 PM, 3:33 PM, ...
But sometime don't receive above result: sometime it is 3:31 PM, 3:32 PM, 3:34 PM, ...
So it lost 1 row.
Could anyone point me what is problem?
class Program
{
static Timer m_Timer;
static int countDown;
static void Main(string[] args)
{
countDown = 60;
m_Timer = new Timer(TimerCallback, null, 0, 1000);
while (true) { System.Threading.Thread.Sleep(10); };
}
static void TimerCallback(Object o)
{
countDown -= 1;
if (countDown <= 0)
{
Console.WriteLine(" ===>>>>>" + System.DateTime.Now.ToString());
countDown = 60;
}
System.Threading.Thread.Sleep(10000); //long running code demo
}
}
System.Threading.Timer runs on threads from thread pool. You run callback function which runs on one thread in pool every 1s and block it for 10s using sleep. Depending on how many threads you have in thread pool at some timepoints they all may be blocked and wait or .NET should allocate new thread up to the maximum of threads in pool for you.
From comments extended answer.
Each function is independent and it does not wait until another processing finish. A simple task is: call a function to do something every 1 minutes. "do something" in my case is saving local variables into SQL server. This process is fast not slow. I use 1 timer for many functions because each function is schedule in different cycle. For example, function 1 is triggered every 1 minute, function 2 is triggered every 10 seconds ... That why I use the timer 1 second.
Your use case seems to be more complex as I read it from initial question. You have different tasks and try to implement sort of scheduler. Maybe each particular tasks is fast but all together some runs may be longer and blocking. Not sure how this logic was well implemented but there could be a lot of edge cases e.g. some run was missed etc.
How I would approach it?
I would not try to implement on my own if scheduler can be more complex. I would pick ready solution, e.g. Quartz.NET. They consider edge cases and help to scale on cluster with needed and help with config.
In any case I would refactor bigger schedule to have each task to run on its schedule based on configuration (custom implementation or Quartz) as smaller tasks
I would scale your "queue" of tasks first locally by introducing some queue, for example using ConcurrentQueue or BlockingCollection or any produce-consumer to limit number of threads and if performance of such execution is not good scale on cluster. By doing so you can at least guarantee that N tasks can be scheduled and executed locally and everything beyond is queued. Maybe having some priorities for tasks can also help because there might be execution which could be missed but there are execution which must run on schedule.
I doubt it is a good idea to start from thread timer execution other threads or tasks if most likely you already have problems with threading.
You problem is not with System.Threading.Timer, it does its job well. Your use case is more complex.
Windows - is not real time operating system. So, if you expect that timer waits ecactly 1 second - it's wrong. There are many reasonsm when timer can wait more time. Because of timer resolution or other high load operations.
If you like newer .NET TPL syntax yo can write it like this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp1
{
internal class Program
{
private static void Main(string[] args)
{
Repeat(TimeSpan.FromSeconds(10));
Console.ReadKey();
}
private static void Repeat(TimeSpan period)
{
Task.Delay(period)
.ContinueWith(
t =>
{
//Do your staff here
Console.WriteLine($"Time:{DateTime.Now}");
Repeat(period);
});
}
}
}
The above code causes, that every second you run 10-second "demo" (sleep). You will run 10 worker threads simultanously.
Are you sure, this is what you are trying to achieve?
To see what really happens in your app, simply add:
Console.WriteLine($"Time:{DateTime.Now.ToString("hh:mm:ss.fff tt")},Thread:{Thread.CurrentThread.ManagedThreadId},countDown:{countDown}");
in the beginning of TimerCallback. You will notice, that timespan between following callbacks are not exactly 1000ms (usually it is a little bit more). This is perfectly normal in non-rtc OS, and, in most cases - it's not a problem. Just keep in mind, that Timer is not exact.
Moreover, if you are trying to use Timer that way, and trying to count ticks - these little errors cumulates in following ticks.
I just post what found here for people that have problem like me.
I found the answer from another thread.
I use "HighResolutionTimer.cs" and it works perfect:
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545

Quartz.net Run 1 instance of a job with different JobKey and JobData

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 it acceptable practice to create Tasks with long delays?

I'm creating a scheduler to fire events at specific times of the day, and to do this I'm spinning up Tasks (one at a time, i.e. the 'next' schedule only) with a Task.Delay of anything up to a few days delay. For example, after the last event fires on a Friday afternoon, I'll set up the next one which will be some time on Monday, so it could potentially be a TimeSpan of up to 3 days (~260,000,000 milliseconds).
Is this acceptable practice? I'm concerned that this won't be stable/robust enough for a production environment.
Here's some snippets of code to describe what I've put together:
private void SetNextEvent()
{
TimeModel next = GetNextScheduledTime();
Debug.WriteLine($"Next schedule [{next.TimeType}]: {next.Time.ToString("yyyy-MM-dd HH:mm:ss")}");
TimeSpan delay = next.Time.Subtract(DateTime.Now);
Task.Run(async () =>
{
await Task.Delay(delay);
FireEvent(next);
});
}
private void FireEvent(TimeModel time)
{
Debug.WriteLine($"Event fired [{time.TimeType}]: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
OnSchedulerEvent?.Invoke(this, new SchedulerEventArgs { ScheduleType = time.TimeType });
if (_running)
SetNextEvent();
}
This is totally reliable. .NET timers are very efficient. The biggest problem is that you must assume that your production apps can exit at any time. The reason that is easiest to understand is a bug that kills the process. Other reasons include reboots, app pool recycles, deployments, ....
So if you can recover your state after being killed this is fine. If you add a comment about specific concerns I'll address them.
It looks like you have a way to recover timers because you apparently can compute the next due time. Under those circumstances it's very safe to do this. You need to ensure that your code is always running e.g. right after a reboot or a crash.
Note, that IIS apps need to tolerate running multiple times concurrently. Otherwise, IIS is a great host for your scenario.
If you're running Windows, I'd use the TaskScheduler to do what you're trying to do.
run taskschd.msc you can use that program to schedule periodic tasks.
There should be an "Create Task..." button in the panel on the right.
I agree that the Windows Task Scheduler is probably the best approach is you know ahead of time the schedule to run the next task.
If you do not know this ahead of time(i.e. the time to wait for the next task can vary) then I would suggest using a timer and an event handler method when the timer expires.
Each time you can set the Interval property of the timer for the time to wait. When the time expires the timer event handler will run and it can execute the ask and reset the Interval for the timer. This seems a little cleaner then a Task.Delay.

Prevent Triggers Being Fired Simultaneously

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.

Quartz trigger based on file existence?

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.

Categories