about misfire in java quartz scheduling - java

I have set a trigger using cronScheduler with misfireInstruction like follows
trigger = newTrigger().withIdentity("autoLockTrigger", "autoLockGroup").startNow() .withSchedule(cronSchedule(croneExpression).withMisfireHandlingInstructionFireAndProceed())
.forJob("autoLockJob","autoLockGroup")
.build();
my quartz.properties is like follows
org.quartz.scheduler.instanceName =MyScheduler
# Configuring ThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 1
org.quartz.threadPool.threadPriority = 9
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
#org.quartz.dataSource.myDS.jndiURL = jdbc/vikas
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://staging:3307/facao
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = toor
org.quartz.dataSource.myDS.maxConnections = 30
#org.quartz.jobStore.nonManagedTXDataSource = myDS
#to store data in string format (name-value pair)
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.misfireThreshold = 60000
In my code if I set some trigger at particular time and if server is in running state then scheduler runs properly but if server is down for the time in which scheduler is suppose to be run and then started after some time then scheduler should run the misfired instruction. But in my case the misfired instruction is not running all the time it runs some time not always so my purpose is not fulfilled. Please give some solution. Thank you in advance.

I am not sure about the cron triggers but for simple triggers yeah,
if the end time of the trigger has been passed then some of the provided misfire instruction
will not work. See the javadoc snippet for more info.
I guess the same would be the case with cron trigger too.
So, it totally depends on what cron expression you use.

Related

Quartz scheduler not inserting its scheduler details in their configured database

I'm using JBoss Application Server, I have two nodes of JBoss AS on different machines, on each node I have deployed two different EARs which contain different quartz.properties, and each EAR has its own database.
EAR 1 quartz.properties file configuration
org.quartz.scheduler.instanceName = Scheduler1
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 1
org.quartz.threadPool.threadPriority = 5
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.dataSource = CI
org.quartz.dataSource.CI.jndiURL = java:jboss/datasources/CI
org.quartz.jobStore.dataSource = nonManagedTX
org.quartz.dataSource.nonManagedTX.jndiURL = java:jboss/datasources/CI
org.quartz.jobStore.nonManagedTXDataSource = nonManagedTX
EAR 2 quartz.properties file configuration
org.quartz.scheduler.instanceName = Scheduler2
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 1
org.quartz.threadPool.threadPriority = 5
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.dataSource = CE
org.quartz.dataSource.CE.jndiURL = java:jboss/datasources/CE
org.quartz.jobStore.dataSource = nonManagedTX
org.quartz.dataSource.nonManagedTX.jndiURL = java:jboss/datasources/CE
org.quartz.jobStore.nonManagedTXDataSource = nonManagedTX
The problem is when I deployed EAR1 and EAR2 on JBoss AS, all configurations of EAR1 will be inserted into EAR2 database and vice versa, below are the tables in which Quartz data are stored.
QRTZ_CRON_TRIGGERS
QRTZ_JOB_DETAILS
QRTZ_LOCKS
QRTZ_SCHEDULER_STATE
QRTZ_SIMPLE_TRIGGERS
QRTZ_TRIGGERS
I have rechecked all the configurations like jndiURL and datasource name etc, but no luck.
Can anyone please tell me why this is happening? How Quartz scheduler inserts triggers of one EAR to another EAR database, if I have different configurations in both the EARs, and how can I resolve this issue?

Quartz trigger does not fire immediately

I'd like to execute the job ~immediately with quartz scheduler using jdbc datastore. However I have like 20-30 seconds delay between the scheduling and trigger fire even though I schedule with now() or calling triggerJob.
I tried to execute the job with a simple trigger:
JobKey key = //...
JobDetail jobDetail = newJob(jobBean.getClass())
.withIdentity(key)
.usingJobData(new JobDataMap(jobParams))
.storeDurably()
.build();
Trigger trigger = newTrigger()
.withIdentity(key.getName(), key.getGroup())
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow()
.withRepeatCount(0))
.build();
scheduler.scheduleJob(jobDetail, trigger);
And I also tried to trigger with scheduler:
JobKey key = // ...
JobDetail jobDetail = newJob(jobBean.getClass())
.withIdentity(key)
.storeDurably()
.build();
scheduler.addJob(jobDetail, true);
scheduler.triggerJob(key, new JobDataMap(jobParams));
Here are the listener logs that shows the delay.
2019-05-15 13:59:52,066Z INFO [nio-8081-exec-2] c.m.f.s.logger.SchedulingListener : Job added: newsJobTemplate:1557928791965
2019-05-15 13:59:52,066Z INFO [nio-8081-exec-2] c.m.f.s.logger.SchedulingListener : Job scheduled: newsJobTemplate:1557928791965
2019-05-15 14:00:18,660Z INFO [eduler_Worker-1] c.m.f.s.logger.TriggerStateListener : Trigger fired: QUARTZ_JOBS.newsJobTemplate:1557928791965 {}
2019-05-15 14:00:18,703Z INFO [eduler_Worker-1] c.m.f.s.logger.JobExecutionListener : Job will be executed: QUARTZ_JOBS.newsJobTemplate:1557928791965
2019-05-15 14:00:19,284Z INFO [eduler_Worker-1] c.m.f.s.logger.JobExecutionListener : Job was executed: QUARTZ_JOBS.newsJobTemplate:1557928791965
I found crumbs here and there that suggested that the problem is transaction related.
So I removed #Transactional from the service method and voila it worked.
Looks like when you call trigger the scheduler thread asyncronously tries to look up schedules and triggers from the DB but the transaction is not committed at that time. Later the scheduler thread looks up the db again and it finds it finally.
zolee's answer describes the problem perfectly, but there are also a few things one can do to solve it.
One imperfect solution is to reduce org.quartz.scheduler.idleWaitTime. In fact, the problem itself is described, though somewhat obliquely, in the quartz configuration doc, org.quartz.scheduler.idleWaitTime section.
Normally you should not have to ‘tune’ this parameter, unless you’re
using XA transactions, and are having problems with delayed firings of
triggers that should fire immediately.
That will allow you to reduce 30-second delay to 5 seconds or even less.
A full solution is to extend QuartzScheduler to add transaction support. Exact implementation will depend on what library/code you're using for transaction support, but it worked for us perfectly.
class TransactionAwareScheduler extends QuartzScheduler {
#Override
protected void notifySchedulerThread(long candidateNewNextFireTime) {
if (insideTransaction) {
transaction.addCommitHook(() -> {
super.notifySchedulerThread(candidateNewNextFireTime);
});
}
} else {
super.notifySchedulerThread(candidateNewNextFireTime);
}
}

Quartz in cluster mode with lockoninsert=false

I have application having 4 nodes. Sometimes all my jobs will be stuck waiting for lock from 'Select * from QRTZ_LOCKS where lock_name='TRIGGER_ACCESS' for update'
While reading some of the articles someone suggested to turn off global lock using this property
org.quartz.jobStore.lockOnInsert=false
Has anyone tried to run Quartz in cluster mode with lockoninsert=false?
I am planning to use following configuration
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = StandardScheduler
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 3
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 300000
org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.lockOnInsert=false
org.quartz.jobStore.acquireTriggersWithinLock=true
org.quartz.jobStore.lockHandler.class=org.quartz.impl.jdbcjobstore.UpdateLockRowSemaphore
org.quartz.scheduler.jmx.export=true
A found some comment in the 1.8.6' source
public boolean isLockOnInsert() {
return lockOnInsert;
}
/**
* Whether or not to obtain locks when inserting new jobs/triggers.
* Defaults to <code>true</code>, which is safest - some db's (such as
* MS SQLServer) seem to require this to avoid deadlocks under high load,
* while others seem to do fine without.
*
* <p>Setting this property to <code>false</code> will provide a
* significant performance increase during the addition of new jobs
* and triggers.</p>
*
* #param lockOnInsert
*/

porting Quartz from IAS to JBoss AS

I am porting our code from IAS to JBoss AS.
There is strange behavior where quartz does not trigger any event at all, and no errors appear at quartz logs. I have also noticed that the Quartz tables are not populated (QRTZ_JOB_DETAILS, QRTZ_TRIGGERS etc.).
I am using JOBStoreCMT with quartz version 1.5.2. datasource is well declared. Jobs and triggers worked well in IAS and declared within the code.
quartz properties:
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = bitbandScheduler
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 15
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = bitband_pluginDS
org.quartz.jobStore.nonManagedTXDataSource = bitband_pluginDSTX
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = false
org.quartz.jobStore.clusterCheckinInterval = 20000
#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.bitband_pluginDS.jndiURL=java:bitband_pluginDS
org.quartz.dataSource.bitband_pluginDSTX.jndiURL=java:bitband_pluginDS
oracle-ds.xml:
<xa-datasource>
<jndi-name>bitband_pluginDS</jndi-name>
<!-- uncomment to enable interleaving <interleaving/> -->
<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:oracle:thin:#ord-rtv063.orca.ent:1521:DB11g</xa-datasource-property>
<xa-datasource-property name="User">RIGHTV7_VS</xa-datasource-property>
<xa-datasource-property name="Password">RIGHTV7_VS</xa-datasource-property>
<max-pool-size>100</max-pool-size>
<min-pool-size>20</min-pool-size>
<valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name>
<check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>
<!-- Uses the pingDatabase method to check a connection is still valid before handing it out from the pool -->
<!--valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name-->
<!-- Checks the Oracle error codes and messages for fatal errors -->
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<!-- Oracles XA datasource cannot reuse a connection outside a transaction once enlisted in a global transaction and vice-versa -->
<no-tx-separate-pools/>
<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</xa-datasource>
What am I missing?
PS, When using JobStoreTX, everything works well, so I guess it is something related to the container transaction manager.
After hanging around the problem for the last couple of days I found a solution.
Adding the property below to quartz.properties file. As simple as that.
org.quartz.jobStore.dontSetAutoCommitFalse=false
Setting this parameter to true tells Quartz not to call setAutoCommit(false) on connections obtained from the DataSource(s). This can be helpful in a few situations, such as if you have a driver that complains if it is called when it is already off. This property defaults to false, because most drivers require that setAutoCommit(false) is called.
For some reason JBoss overrides the default value, so I had to add it explicitly.
The credit goes to unknown user at:
http://osdir.com/ml/java.quartz.user/2007-10/msg00123.html

Quartz Enterprise Scheduler: Job that schedules itself

I am using Quartz Enterprise Job Scheduler (1.8.3). The job configuration comes from several xml files and we have a special job that detects changes in these xml files and re-schedules jobs. This works dandy, but the problem is that I also need this "scheduler job" to re-schedule itself. Once this job re-schedules itself, for some reason, I see that it gets executed many times. I don't see any exceptions, though.
I have replicated and isolated the problem. This would be the entry-point:
public class App {
public static void main(final String[] args) throws ParseException, SchedulerException {
// get the scheduler from the factory
final Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// start the scheduler
scheduler.start();
// schedule the job to run every 20 seconds
final JobDetail jobDetail = new JobDetail("jobname", "groupname", TestJob.class);
final Trigger trigger = new CronTrigger("triggername", "groupname", "*/20 * * * * ?");
// set the scheduler in the job data map, so the job can re-configure itself
jobDetail.getJobDataMap().put("scheduler", scheduler);
// schedule job
scheduler.scheduleJob(jobDetail, trigger);
}
}
And this would be the job class:
public class TestJob implements Job {
private final static Logger LOG = Logger.getLogger(TestJob.class);
private final static AtomicInteger jobExecutionCount = new AtomicInteger(0);
public void execute(final JobExecutionContext context) throws JobExecutionException {
// get the scheduler from the data map
final Scheduler scheduler = (Scheduler) context.getJobDetail().getJobDataMap().get("scheduler");
LOG.info("running job! " + jobExecutionCount.incrementAndGet());
// buid the job detail and trigger
final JobDetail jobDetail = new JobDetail("jobname", "groupname", TestJob.class);
// this time, schedule it to run every 35 secs
final Trigger trigger;
try {
trigger = new CronTrigger("triggername", "groupname", "*/50 * * * * ?");
} catch (final ParseException e) {
throw new JobExecutionException(e);
}
trigger.setJobName("jobname");
trigger.setJobGroup("groupname");
// set the scheduler in the job data map, so this job can re-configure itself
jobDetail.getJobDataMap().put("scheduler", scheduler);
try {
scheduler.rescheduleJob(trigger.getName(), jobDetail.getGroup(), trigger);
} catch (final SchedulerException e) {
throw new JobExecutionException(e);
}
}
}
I've tried both with scheduler.rescheduleJob and with scheduler.deleteJob then scheduler.scheduleJob. No matter what I do, this is the output I get (I'm using log4j):
23:22:15,874 INFO SchedulerSignalerImpl:60 - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
23:22:15,878 INFO QuartzScheduler:219 - Quartz Scheduler v.1.8.3 created.
23:22:15,883 INFO RAMJobStore:139 - RAMJobStore initialized.
23:22:15,885 INFO QuartzScheduler:241 - Scheduler meta-data: Quartz Scheduler (v1.8.3)
'MyScheduler' with instanceId '1'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
23:22:15,885 INFO StdSchedulerFactory:1275 - Quartz scheduler 'MyScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
23:22:15,886 INFO StdSchedulerFactory:1279 - Quartz scheduler version: 1.8.3
23:22:15,886 INFO QuartzScheduler:497 - Scheduler MyScheduler_$_1 started.
23:22:20,018 INFO TestJob:26 - running job! 1
23:22:50,004 INFO TestJob:26 - running job! 2
23:22:50,010 INFO TestJob:26 - running job! 3
23:22:50,014 INFO TestJob:26 - running job! 4
23:22:50,016 INFO TestJob:26 - running job! 5
...
23:22:50,999 INFO TestJob:26 - running job! 672
23:22:51,000 INFO TestJob:26 - running job! 673
Notice how at 23:22:20,018, the job runs fine. At this point, the job re-schedules itself to run every 50 seconds. The next time it runs (at 23:22:50,004), it gets scheduled hundreds of times.
Any ideas on how to configure a job while executing that job? What am I doing wrong?
Thanks!
Easy.
First off you have a couple misunderstandings about Cron Expressions. "*/20 * * * * ?" is every twenty seconds as the comment implies, but only because 60 is evenly divisible by 20. "/50 ..." is not every fifty seconds. it is seconds 0 and 50 of every minute. As another example, "/13 ..." is seconds 0, 13, 26, 39, and 52 of every minute - so between second 52 and the next minute's 0 second, there is only 8 seconds, not 13. So with */50 you'll get 50 seconds between every other firing, and 10 seconds between the others.
That however is not the cause of your rapid firing of the job. The problem is that the current second is "50" and you are scheduling the new trigger to fire on second "50", so it immediately fires. And then it is still second 50, and the job executes again, and it schedules another trigger to fire on second 50, and so on, as many times as it can during the 50th second.
You need to set the trigger's start time into the future (at least one second) or it will fire on the same second you are scheduling it, if the schedule matches the current second.
Also if you really need every "N" seconds type of schedule, I suggest SimpleTrigger rather than CronTrigger. SimpleTrigger can do "every 35 seconds" or "every 50 seconds" no problem. CronTrigger is meant for expressions like "on seconds 0, 15, 40 and 43 of minutes 15 and 45 of the 10 o'clock hour on every Monday of January".

Categories

Resources