Quartz cluster - load balancing doesn't work - spring

I have a problem with load-balancing Quartz jobs. When there are two instances running, only one of them handles all jobs. The second one is idling. When I terminate first instance the second one starts to handle jobs until first instance is started again.
I expected that there is kind of load-balancing which dispatches jobs between those two instances.
I am using Quartz version 1.8.6.
This is the part of applicationContext.xml:
<bean id="firstJobDetail"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mycompany.quartz.job.FirstJob" />
<property name="durability" value="true" />
</bean>
<bean id="firstTrigger" class="com.mycompany.quartz.PersistableCronTriggerFactoryBean">
<property name="jobDetail" ref="firstJobDetail" />
<property name="cronExpression" value="0/10 * * * * ?" />
</bean>
<bean id="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:META-INF/quartz.properties" />
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<!-- This name is persisted as SCHED_NAME in db. for local testing could
change to unique name to avoid collision with dev server -->
<property name="schedulerName" value="quartzScheduler" />
<!-- Will update database cron triggers to what is in this jobs file on
each deploy. Replaces all previous trigger and job data that was in the database.
YMMV -->
<property name="overwriteExistingJobs" value="true" />
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<property name="jobFactory">
<bean class="com.mycompany.quartz.AutowiringSpringBeanJobFactory" />
</property>
<!-- NOTE: Must add both the jobDetail and trigger to the scheduler! -->
<property name="jobDetails">
<list>
<ref bean="firstJobDetail" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="firstTrigger" />
</list>
</property>
</bean>
And this is the quartz.properties file:
# Spring uses LocalDataSourceJobStore extension of JobStoreCMT
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
# Change this to match your DB vendor
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# Needed to manage cluster instances
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=MY_JOB_SCHEDULER
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

According to the documentation, you need to set org.quartz.jobStore.clusterCheckinInterval .

I Know it is too late to answer your question. But recently I come across the same issue and found your post in online.
Whenever there are two instances running, either one of them tries to put lock on the job. Since your trigger time is every 10 secs which is very less (0/10 * * * * ?), the instance 2 is unable to put lock on the job.
Increase the time period to one minute(* 0/1 * * * ?). You can see, both the instance will process the job.
Please let me know if you face any other issues.

Documentation says:
Load-balancing occurs automatically, with each node of the cluster
firing jobs as quickly as it can. [...] Only one node will fire the
job for each firing. [...] It won’t necessarily be the same node each
time - it will more or less be random which node runs it. The load
balancing mechanism is near-random for busy schedulers (lots of
triggers) but favors the same node for non-busy (e.g. few triggers)
schedulers.

Related

How do we add startTime in springframework.scheduling.quartz.CronTriggerFactoryBean?

We need to add the startTime for spring quartz scheduler. Lets assume the below bean as CronTriggerFactoryBean. And we need to start the scheduler after 5 days of server starting time.
<bean id="sampleBean"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="beanManager"/>
<property name="targetMethod" value="beanMethod"/>
<property name="concurrent" value="false"/>
</bean>
</property>
<property name="cronExpression" value="0 0/30 * ? * SUN-FRI"/>
</bean>
And above scheduler configuration doesn't start when application server get started. Its running like
Let's assume server start at 12:03 PM.
Scheduler invoking at 12:30, 13:00, 13:30, 14:00 ... respectively.
But what we want is server start at 12:03 PM means the scheduler should run once at the time of server starting and the following time too 12:33, 13:03, 13:33, 14:03 ...
Will it work when we are setting the startTime for this bean?
You should use SimpleTriggerFactoryBean instead of CronTriggerFactoryBean and define a trigger like this:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="simpleJobDetail" />
<property name="startDelay" value="1000" />
<property name="repeatInterval" value="10000" />
</bean>
This will run the job every 10 seconds with initial delay of 1 second from server start. Now adjust the values as per your requirements.

How to force beans in quartz scheduler to be initialized on start up?

<bean name="MyJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.package.scheduler.MyJob"/>
<property name="jobDataAsMap">
<map>
<entry key="numArr" value="10,20,30"/>
</map>
</property>
</bean>
I have a quartz job defined that runs this at 1:30am. The problem is this array definition does not work
<entry key="numArr" value="10,20,30"/>
We had to use list instead. Since the beans do not get initialized until the quartz job kicks in and it's unlikely we would be coding at 1:30am, we cannot catch this error locally. The only way right now is to change the quartz job's running time to force it to happen. We want to force these beans to be initialized on startup when we are running on local dev. Is there a way to do this?
If possible I would decouple your bean from the Quartz job. You could then intialise your bean separately in your spring configuration and it does not need to be made aware of Quartz. I would then use the spring MethodInvokingJobDetailFactoryBean to call a method on your decoupled object.
For example something similar to this:
<bean id="myBean class="...">
<property name="numArr" value="10,20,30"/>
</bean>
<bean id="myJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myBean" />
<property name="targetMethod" value="doSomething" />
</bean>
<bean id="staleTraderRatesTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="myJob" />
<property name="cronExpression" value="0 30 1 * * ?" />
</bean>

quartz jobs in a cluster running on wrong node

uI have an application that is clustered in a 2+2 setup where 2 machines are used in alpha product phase, and the other two are used for real customers. all machines are looking at the same database,
I need a job to run at midnight for each of the groups. one of the two alpha machines should get a job, and the other two should get another job.
I'm using Spring 3.0.5 with Quartz 1.8.5 with the following properties
phase=alpha
quartz.job.name=MY_JOB_${phase}
<bean id="quartzPropertiesFactoryBean" class="com.liveperson.kwo.quartz.QuartzPropertiesFactory">
<constructor-arg value="AUTO"/>
<constructor-arg value="MY_CLUSTER"/>
<constructor-arg value="JobStoreTX"/>
<constructor-arg value="StdJDBCDelegate"/>
<constructor-arg value="true"/> //isClustered
<constructor-arg value="false"/> //useProperties
</bean>
<bean name="runJobBean" class="org.quartz.JobDetail">
<property name="name" value="${quartz.job.name}"/>
<property name="jobClass" value="CLASS1"/>
<property name="group" value="JOB-for-${quartz.job.name}"/>
<property name="jobDataMap">
<bean class="org.quartz.JobDataMap">
</bean>
</property>
</bean>
<bean name="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="quartzProperties" ref="quartzPropertiesFactoryBean"/>
<property name="dataSource" ref="mySqlConnectorBean"/>
<property name="overwriteExistingJobs" value="true"/>
<property name="jobDetails">
<list>
<ref bean="runJobBean"/>
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTriggerBean"/>
</list>
</property>
</bean>
I define two jobs, one for alpha and another for production throw the phase property, and the problem I'm having is that the job I define for alpha phase runs on the node defined to production, how can I make the job defined for the alpha phase run only on machines defined for alpha?
Thanks!

Variables values remain in memory after Spring Batch Job execution

My project has several processes that are been executed each day. The problem I found is that after a job execution, when I execute again the same process (with different job parameters, of course) I see Spring batch generates a new Job instance BUT the variables values remain in memory for the new execution.
How is it possible? New instances don't create new Java instances? Problem configuration?
My JobLoader configuration:
<bean id="jobLoader" class="org.springframework.batch.core.configuration.support.AutomaticJobRegistrar">
<property name="applicationContextFactories">
<bean class="org.springframework.batch.core.configuration.support.ClasspathXmlApplicationContextsFactoryBean">
<property name="resources" value="classpath*:/META-INF/spring/batch/jobs/*.xml" />
</bean>
</property>
<property name="jobLoader">
<bean class="org.springframework.batch.core.configuration.support.DefaultJobLoader">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
</property>
</bean>
Thanks,

Spring Quartz Scheduling

In my application there is a requirement to be able to create Scheduled Job(s) for My sql database autobackup
Can I use Spring Quartz Scheduling to create this Jobs?
Any help would be useful.
Amulraj.P
Yes you can, though it seems a bit overkill. Backuping an MSSQL db can be done using commandline tools, which you can easily schedule to run using cron if you are using Unix or Scheduled Tasks on Windows.
%PATH_TO_SQL_SERVER%\Tools\Binn\osql.exe
-E -Q "BACKUP DATABASE mydb TO DISK='%PATH_TO_BKP%\db.bak' WITH FORMAT"
For you question the answer is something like this: (shameless self-copy from here)
The job referring to your business object which has the method which takes care of the backup:
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="exampleBusinessObject" />
<property name="targetMethod" value="backupDB" />
<property name="concurrent" value="false" />
</bean>
The trigger that takes care of firing the method:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="exampleJob" />
<!-- run every morning at 6 AM, use regular cron expressions-->
<property name="cronExpression" value="0 0 6 * * ?" />
</bean>
The schedulerFactoryBean for wiring the trigger:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
See further in Spring documentation for 2.5, here for 3.0.

Resources