Issue Details (XML | Word | Printable)

Key: QRTZNET-122
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Critical Critical
Assignee: Marko Lahma
Reporter: Phil Hunt
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Quartz.NET

Triggering a job remotely often does not fire the job

Created: 29/Aug/08 04:03 PM   Updated: 06/Sep/08 03:17 AM
Component/s: Core
Affects Version/s: 1.0 RC 2
Fix Version/s: 1.0 RC 3

Environment: Windows XP, VS.NET 2008 .NET 3.5

Flags: Important


 Description  « Hide
Remotely (out of process) calling Scheduler.TriggerJob() or TriggerJobWithVolatileTrigger(). Sometimes the job triggers immediately. Other times the job does not trigger until one of the other schedules for the job triggers, then both triggers fire. Interestingly, it seems to be time related like if the next trigger is supposed to happen soon (within 30 seconds) it will queue up both.

I was able to get it to work reliably by modifying QuartzSchedulerThread::IsCandidateNewTimeEarlierWithinReason. The original code is calculating the difference as
TimeSpan diff = DateTime.UtcNow - oldTimeUtc;

which will normally return a negative span since hte oldTimeUtc is the next scheduled item to run which is in the future. Changing this to
TimeSpan diff = oldTimeUtc - DateTime.UtcNow;

Seems to work reliably and will still support the concept of not having to re-aquire the new trigger if the old trigger is about to fire.

 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Marko Lahma added a comment - 30/Aug/08 08:35 AM
I feel uncomfortable changing this behavior. There has been some changes to code to fix threading issues and trigger firings. Could you test with latest SVN trunk version?

Phil Hunt added a comment - 31/Aug/08 11:54 AM
I tried with the latest trunk and I am still receiving the same results.

I created a very simple job (that just logs to the common logger).

public void Execute(JobExecutionContext context) {

    try {

        if (Logger.IsInfoEnabled) {
            Logger.Info("Execute(JobExecutionContext) started. " + context.Trigger.ToString());
        }
        context.Result = "Success";
    } catch (Exception exc) {
        throw new JobExecutionException(exc);
    }
}

The quartz section of my configuration file is as follows.

<quartz>
    <add key="quartz.scheduler.instanceName" value="QuartzScheduler" />

    <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
    <add key="quartz.threadPool.threadCount" value="10" />
    <add key="quartz.threadPool.threadPriority" value="2" />

    <add key="quartz.jobStore.misfireThreshold" value="60000" />
    <add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />

    <add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.JobInitializationPlugin, Quartz" />
    <add key="quartz.plugin.xml.fileNames" value="~/quartz_jobs.xml" />

    <add key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz" />
    <add key="quartz.scheduler.exporter.port" value="555" />
    <add key="quartz.scheduler.exporter.bindName" value="QuartzScheduler.rem" />
    <add key="quartz.scheduler.exporter.channelType" value="tcp" />
  </quartz>

I then have a separate Windows application that connects to the scheduler application that has the following code.
private void TriggerJobButton_Click(object sender, EventArgs e) {
    this.mScheduler.TriggerJobWithVolatileTrigger("sampleJob", "sampleGroup"); }

If I schedule a Simple trigger to execute every 30,000 seconds, then I use the separate Windows application and fire the job manually, it seems to hold the manual job and run it the next time another trigger fires every time. I have not been able to get it to fire manually without waiting on the next scheduled job. As a matter of fact, if I attempt to trigger manually multiple times, it will trigger the job each time but ONLY when the next scheduled trigger fires. However, if I change the Simple trigger to be every 300,000 ms, it seems to work most of the time. However, I attempt to manually trigger a job within about 30 seconds of another scheduled trigger, the manual job waits to execute until the scheduled job executes.

My Quartz_Jobs.xml is below.

<?xml version="1.0" encoding="UTF-8"?>
<quartz xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  version="1.0"
overwrite-existing-jobs="true">
  <calendar type="Quartz.Impl.Calendar.AnnualCalendar, Quartz" replace="false">
    <name>cal1</name>
    <description>description0</description>
    <base-calendar type="Quartz.Impl.Calendar.WeeklyCalendar, Quartz" replace="false">
      <name>name1</name>
      <description>description1</description>
    </base-calendar>
  </calendar>

  <job-listener type="QuartzIntegration.JobListener, QuartzIntegration" name="iSolutions Job Listener"/>
  
  <job>
    <job-detail>
      <name>sampleJob</name>
      <group>sampleGroup</group>
      <description>Sample job for Quartz Server</description>
      <job-type>QuartzIntegration.LoggingJob, QuartzIntegration</job-type>
      <job-listener-ref>iSolutions Job Listener</job-listener-ref>
      <volatile>false</volatile>
      <durable>true</durable>
      <recover>false</recover>
    </job-detail>
    <trigger>
      <simple>
        <name>sampleSimpleTrigger</name>
        <group>sampleSimpleGroup</group>
        <description>Simple trigger to simply fire sample job</description>
        <misfire-instruction>SmartPolicy</misfire-instruction>
        <volatile>false</volatile>
        <job-name>sampleJob</job-name>
        <job-group>sampleGroup</job-group>
        <repeat-count>RepeatIndefinitely</repeat-count>
        <repeat-interval>30000</repeat-interval>
      </simple>
    </trigger>
    <trigger>
      <cron>
        <name>sampleCronTrigger</name>
        <group>sampleCronGroup</group>
        <description>Simple cron to simply fire sample job</description>
        <misfire-instruction>SmartPolicy</misfire-instruction>
        <job-name>sampleJob</job-name>
        <job-group>sampleGroup</job-group>
        <cron-expression>0/360 * * * * ?</cron-expression>
      </cron>
    </trigger>
  </job>
  
</quartz>

Below is a sample from the log file when it DOES not work. In both instances of the LoggingJob being calld by the Trigger 'MANUAL_TRIGGER.xxx' I triggered the job at least 20 seconds before the next scheduled job.

12:41:58.006 DEBUG Quartz.Simpl.SimpleJobFactory - Producing instance of Job 'sampleGroup.sampleJob', class=QuartzIntegration.LoggingJob
12:41:58.006 DEBUG Quartz.Simpl.SimpleJobFactory - Producing instance of Job 'sampleGroup.sampleJob', class=QuartzIntegration.LoggingJob
12:41:58.006 DEBUG Quartz.Core.JobRunShell - Calling Execute on job sampleGroup.sampleJob
12:41:58.006 INFO QuartzIntegration.LoggingJob - Execute(JobExecutionContext) started. Trigger 'sampleSimpleGroup.sampleSimpleTrigger': triggerClass: 'Quartz.SimpleTrigger isVolatile: False calendar: '' misfireInstruction: 0 nextFireTime: 08/31/2008 16:42:28
12:41:58.006 DEBUG Quartz.Core.JobRunShell - Trigger instruction : NoInstruction
12:41:58.006 DEBUG Quartz.Core.JobRunShell - Calling Execute on job sampleGroup.sampleJob
12:41:58.006 INFO QuartzIntegration.LoggingJob - Execute(JobExecutionContext) started. Trigger 'MANUAL_TRIGGER.MT_5934449982116256437': triggerClass: 'Quartz.SimpleTrigger isVolatile: True calendar: '' misfireInstruction: 0 nextFireTime:
12:41:58.006 DEBUG Quartz.Core.JobRunShell - Trigger instruction : DeleteTrigger
12:41:58.006 DEBUG Quartz.Simpl.RAMJobStore - Deleting trigger
12:41:58.006 DEBUG Quartz.Simpl.RAMJobStore - RemoveTrigger MT_5934449982116256437,MANUAL_TRIGGER
12:42:00.005 DEBUG Quartz.Simpl.SimpleJobFactory - Producing instance of Job 'sampleGroup.sampleJob', class=QuartzIntegration.LoggingJob
12:42:00.005 DEBUG Quartz.Core.JobRunShell - Calling Execute on job sampleGroup.sampleJob
12:42:00.005 INFO QuartzIntegration.LoggingJob - Execute(JobExecutionContext) started. Trigger 'sampleCronGroup.sampleCronTrigger': triggerClass: 'Quartz.CronTrigger isVolatile: False calendar: '' misfireInstruction: 0 nextFireTime: 08/31/2008 16:43:00
12:42:00.005 DEBUG Quartz.Core.JobRunShell - Trigger instruction : NoInstruction 12:42:28.010 DEBUG Quartz.Simpl.SimpleJobFactory - Producing instance of Job 'sampleGroup.sampleJob', class=QuartzIntegration.LoggingJob
12:42:28.010 DEBUG Quartz.Core.JobRunShell - Calling Execute on job sampleGroup.sampleJob 12:42:28.010 INFO QuartzIntegration.LoggingJob - Execute(JobExecutionContext) started. Trigger 'sampleSimpleGroup.sampleSimpleTrigger': triggerClass: 'Quartz.SimpleTrigger isVolatile: False calendar: '' misfireInstruction: 0 nextFireTime: 08/31/2008 16:42:58 12:42:28.010 DEBUG Quartz.Core.JobRunShell - Trigger instruction : NoInstruction
12:42:58.013 DEBUG Quartz.Simpl.SimpleJobFactory - Producing instance of Job 'sampleGroup.sampleJob', class=QuartzIntegration.LoggingJob
12:42:58.013 DEBUG Quartz.Core.JobRunShell - Calling Execute on job sampleGroup.sampleJob
12:42:58.013 DEBUG Quartz.Simpl.SimpleJobFactory - Producing instance of Job 'sampleGroup.sampleJob', class=QuartzIntegration.LoggingJob
12:42:58.013 INFO QuartzIntegration.LoggingJob - Execute(JobExecutionContext) started. Trigger 'sampleSimpleGroup.sampleSimpleTrigger': triggerClass: 'Quartz.SimpleTrigger isVolatile: False calendar: '' misfireInstruction: 0 nextFireTime: 08/31/2008 16:43:28
12:42:58.013 DEBUG Quartz.Core.JobRunShell - Trigger instruction : NoInstruction
12:42:58.013 DEBUG Quartz.Core.JobRunShell - Calling Execute on job sampleGroup.sampleJob
12:42:58.013 INFO QuartzIntegration.LoggingJob - Execute(JobExecutionContext) started. Trigger 'MANUAL_TRIGGER.MT_1281467642822479981': triggerClass: 'Quartz.SimpleTrigger isVolatile: True calendar: '' misfireInstruction: 0 nextFireTime:
12:42:58.013 DEBUG Quartz.Core.JobRunShell - Trigger instruction : DeleteTrigger
12:42:58.013 DEBUG Quartz.Simpl.RAMJobStore - Deleting trigger
12:42:58.013 DEBUG Quartz.Simpl.RAMJobStore - RemoveTrigger MT_1281467642822479981,MANUAL_TRIGGER

Marko Lahma added a comment - 06/Sep/08 03:17 AM
Yes, your solution seems to be perfectly valid and seems to fix the problem. Fix is available in trunk now. Thanks for your detailed problem report.