I have a setup where several servers start @Schedule , which start the spring batch job, which sends emails to users. I want to make sure that only one instance of this task runs across multiple servers.
Based on this question, I implemented some logic to see if it can be solved using only the spring package.
To complete the job, I created a helper class JobRunner with the following methods:
public void run(Job job) { try { jobLauncher.run(job, new JobParameters()); } catch (JobExecutionAlreadyRunningException e) { // Check if job is inactive and stop it if so. stopIfInactive(job); } catch (JobExecutionException e) { ... } }
stopIfInactive method:
private void stopIfInactive(Job job) { for (JobExecution execution : jobExplorer.findRunningJobExecutions(job.getName())) { Date createTime = execution.getCreateTime(); DateTime now = DateTime.now(); // Get running seconds for more info. int seconds = Seconds .secondsBetween(new DateTime(createTime), now) .getSeconds(); LOGGER.debug("Job '{}' already has an execution with id: {} with age of {}s", job.getName(), execution.getId(), seconds); // If job start time exceeds the execution window, stop the job. if (createTime.before(now.minusMillis(EXECUTION_DEAD_MILLIS) .toDate())) { LOGGER.warn("Execution with id: {} is inactive, stopping", execution.getId()); execution.setExitStatus(new ExitStatus(BatchStatus.FAILED.name(), String.format("Stopped due to being inactive for %d seconds", seconds))); execution.setStatus(BatchStatus.FAILED); execution.setEndTime(now.toDate()); jobRepository.update(execution); } } }
And then on all servers the following tasks are performed:
@Scheduled(cron = "${email.cron}") public void sendEmails() { jobRunner.run(emailJob); }
Is this a valid solution for setting up multiple servers? If not, what are the alternatives?
EDIT 1
I did a bit more testing - set up two applications that run @Schedule every 5 seconds that trigger the task using the helper class that I created. It seems that my solution does not solve the problem. Here is the data from the batch_job_execution table used by spring:
job_execution_id | version | job_instance_id | create_time | start_time | end_time | status | exit_code | exit_message | last_updated | job_configuration_location ------------------+---------+-----------------+-------------------------+-------------------------+-------------------------+-----------+-----------+--------------+-------------------------+---------------------------- 1007 | 2 | 2 | 2016-08-25 14:43:15.024 | 2016-08-25 14:43:15.028 | 2016-08-25 14:43:16.84 | COMPLETED | COMPLETED | | 2016-08-25 14:43:16.84 | 1006 | 1 | 2 | 2016-08-25 14:43:15.021 | 2016-08-25 14:43:15.025 | | STARTED | UNKNOWN | | 2016-08-25 14:43:15.025 | 1005 | 2 | 2 | 2016-08-25 14:43:10.326 | 2016-08-25 14:43:10.329 | 2016-08-25 14:43:12.047 | COMPLETED | COMPLETED | | 2016-08-25 14:43:12.047 | 1004 | 2 | 2 | 2016-08-25 14:43:10.317 | 2016-08-25 14:43:10.319 | 2016-08-25 14:43:12.03 | COMPLETED | COMPLETED | | 2016-08-25 14:43:12.03 | 1003 | 2 | 2 | 2016-08-25 14:43:05.017 | 2016-08-25 14:43:05.02 | 2016-08-25 14:43:06.819 | COMPLETED | COMPLETED | | 2016-08-25 14:43:06.819 | 1002 | 2 | 2 | 2016-08-25 14:43:05.016 | 2016-08-25 14:43:05.018 | 2016-08-25 14:43:06.811 | COMPLETED | COMPLETED | | 2016-08-25 14:43:06.811 | 1001 | 2 | 2 | 2016-08-25 14:43:00.038 | 2016-08-25 14:43:00.042 | 2016-08-25 14:43:01.944 | COMPLETED | COMPLETED | | 2016-08-25 14:43:01.944 | 1000 | 2 | 2 | 2016-08-25 14:43:00.038 | 2016-08-25 14:43:00.041 | 2016-08-25 14:43:01.922 | COMPLETED | COMPLETED | | 2016-08-25 14:43:01.922 | 999 | 2 | 2 | 2016-08-25 14:42:55.02 | 2016-08-25 14:42:55.024 | 2016-08-25 14:42:57.603 | COMPLETED | COMPLETED | | 2016-08-25 14:42:57.603 | 998 | 2 | 2 | 2016-08-25 14:42:55.02 | 2016-08-25 14:42:55.023 | 2016-08-25 14:42:57.559 | COMPLETED | COMPLETED | | 2016-08-25 14:42:57.559 | (10 rows)
I also tried the method provided by @Palcente, I have similar results.