Reputation: 1422
We have implemented quartz for scheduling.Every job produced have different key.It was working fine till now. Yesterday we come through a problem as same job is being executed twice or thrice(no particular behaviour) by two different Quartz-Worker threads. We cant make thread pool size one as we need concurrent jobs.
One noticeable thing about our scheduled job is that it reschedules(daily, weekly or monthly) itself at every run i.e. if a job is scheduled to run daily then it would reschedule itself in next 24 hours but with a random predefined(say 3 hours) time window. For example if a job ran today at 4:10 (i.e between 4:00 and 7:00) then our job will reschedule it self to tomorrow at some random time between 4:00 and 7:00. It may be 4:01 or 6:59 or 5:23 or any other value in the given time window. This process was also working fine and it is still working fine for most of the cases except in some cases where our rescheduling algo is failing to schedule itself in next 24 hours. Instead it is scheduling itself in next 10 sec, 1 hr or any other random value. But it finally stabilizes itself after 2-3 such wrong rescheduling i.e. it finally schedule itself in next 24 hrs. We are suspecting that this may be happening due to the multiple threads accessing the Calendar object (we are using Calendar.getInstance() and cal.add(Calendar.DAY_OF_YEAR, 1) to reschedule the job in next 24 hours). Somehow calendar instance is picking wrong time or is not able to add one day in current time.
So, there are two issues: 1. Multiple Quartz threads acquiring the same job 2. Calendar is not able to add given interval or is picking wrong current time in some particular cases(multiple thread access)
Any help will be appreciated.Reply asap. Thanks.
Thanks for reply. I would like to know what is the difference between Statefuljob and @DisallowConcurrentExecution annotation and setting threadPool.threadCount to 1.
Code for rescheduling is as...
Calendar cal = Calendar.getInstance();
Calendar nextCal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone(obj.getTimeZone()));
nextCal.setTimeZone(TimeZone.getTimeZone(obj.getTimeZone()));
Date startTime = null;
SimpleTrigger trigger = null;
JobDataMap dataMap = new JobDataMap();
if (repeatTimeInMillis == null) {
cal.set(Calendar.HOUR_OF_DAY, obj.getStartTime());
nextCal.set(Calendar.HOUR_OF_DAY, obj.getStartTime());
cal.set(Calendar.MINUTE, 0);
nextCal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
nextCal.set(Calendar.SECOND, 0);
if (obj.getScheduleType() == ScheduleType.MONTHLY) { // Monthly
log.info("in monthly schedule");
nextCal.add(Calendar.MONTH, 2);
nextCal.set(Calendar.DAY_OF_MONTH, obj.getDate());
cal.add(Calendar.MONTH, 1);
cal.set(Calendar.DAY_OF_MONTH, obj.getDate());
} else if (obj.getScheduleType() == ScheduleType.WEEKLY) { // Weekly
log.info("in weekly schedule");
nextCal.add(Calendar.WEEK_OF_YEAR, 2);
nextCal.set(Calendar.DAY_OF_WEEK, obj.getDay());
cal.add(Calendar.WEEK_OF_YEAR, 1);
cal.set(Calendar.DAY_OF_WEEK, obj.getDay());
} else if (obj.getScheduleType() == ScheduleType.DAILY) { // Daily
log.info("in daily schedule");
nextCal.add(Calendar.DAY_OF_YEAR, 2);
cal.add(Calendar.DAY_OF_YEAR, 1);
}
long time = obj.getTimeWindow() * 60 * 60 * 1000;
time = Math.round(time * Math.random());
cal.setTimeInMillis(cal.getTimeInMillis() + time);
startTime = cal.getTime();
nextCal.setTimeInMillis(nextCal.getTimeInMillis() + time);
repeatTimeInMillis = nextCal.getTimeInMillis() - cal.getTimeInMillis();
log.info("Rescheduling job at " + startTime);
trigger = newTrigger().usingJobData(dataMap)
.withIdentity(obj.getScheduleJobName(), obj.getScheduleJobGroup()).startAt(startTime)
.withSchedule(simpleSchedule().withIntervalInMilliseconds(repeatTimeInMillis).repeatForever())
.build();
} else {
log.info("Rescheduling job next " + repeatTimeInMillis + " milliseconds.");
cal.setTimeInMillis(cal.getTimeInMillis() + repeatTimeInMillis);
startTime = cal.getTime();
trigger = newTrigger().usingJobData(dataMap)
.withIdentity(obj.getScheduleJobName(), obj.getScheduleJobGroup()).startAt(startTime)
.withSchedule(simpleSchedule().withIntervalInMilliseconds(repeatTimeInMillis).withRepeatCount(1)).build();
}
Upvotes: 6
Views: 6351
Reputation: 937
The StatefulJob interface and @DisallowConcurrentExecution annotation do the same thing.
From the DisallowConcurrentExecution javadoc:
marks a Job class as one that must not have multiple instances executed concurrently....
This can be used in lieu of implementing the StatefulJob marker interface that was used prior to Quartz 2.0
Setting the threadPool.threadCount property to 1 would mean at most 1 job of any type can be executing
Using any of these solutions will stop a job executing concurrently and cause any trigger to be placed into a queue to be executed when the previous trigger instance has completed
Upvotes: 3