Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicate Spring Batch Job Instance

I have a small sample Spring Batch application that when started for the first time will work fine but whenever I shut the application down & restart the jar I always get this error:

Caused by: org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; SQL [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]; Duplicate entry '1' for key 1; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 1

I'm not sure if I have the job incrementer setup wrong. But like I said I can start it up & then use the web service url, /jobLauncher.html, to invoke the batch process any number of times just fine. It's only after I shutdown the application & restart it that I get this error. It wants to use id 1 for the job execution table but id 1 is already there from the previous runs.

Main class

@EnableAutoConfiguration
@ComponentScan
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, new String[]{ "date=" + System.currentTimeMillis() });
    }
}

Webservice class

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

    @RequestMapping("/jobLauncher.html")
    @ResponseBody
    public String handle() throws Exception{
        jobLauncher.run(job, new JobParametersBuilder().addString("date", System.currentTimeMillis() + "").toJobParameters());
        return "Started the batch...";
    }
}

Spring Batch class

@Configuration
@EnableBatchProcessing
public class SampleBatchApplication {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    protected Tasklet tasklet() {
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution contribution,
                                        ChunkContext context) {
                return RepeatStatus.FINISHED;
            }
        };
    }

    @Bean
    public Job job() throws Exception {
        return this.jobs.get("job")
                   .incrementer(new RunIdIncrementer())
                   .flow(this.step1())
                   .end()
                   .build();
    }

    @Bean
    protected Step step1() throws Exception {
        return this.steps.get("step1").tasklet(this.tasklet()).build();
    }

    @Bean
    public DataSource dataSource() {

        BasicDataSource ds = new BasicDataSource();

        try {
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUsername("test");
            ds.setPassword("test");
            ds.setUrl("jdbc:mysql://127.0.0.1:3306/spring-batch");

        } catch (Exception e) {
            e.printStackTrace();
        }
        return ds;
    }
}
like image 793
Mark Spangler Avatar asked Oct 25 '13 19:10

Mark Spangler


People also ask

What is Spring Batch job identification?

Job identification: Spring Batch prevents duplicate and concurrent job executions based on the identity of the job instance. Failure scenario: Spring Batch relies on the job instance’s identity to start a new job execution where the previous one left off. Batch processing is about processing fixed, immutable data sets.

Can I automate Spring Batch’s job execution?

You can automate this, as long as the job execution is shut down gracefully, since this gives Spring Batch a chance to correctly set the job execution’s status to FAILED and set its END_TIME to a non-null value. However, if the job execution fails abruptly, the job execution’s status is still be set to STARTED and its END_TIME is null.

How do I restart a failed Spring Batch job?

Graceful/Abrupt Shutdown Implication When a Spring Batch job execution fails, you can restart it if the job instance is restartable. You can automate this, as long as the job execution is shut down gracefully, since this gives Spring Batch a chance to correctly set the job execution’s status to FAILED and set its END_TIME to a non-null value.

What is the batch Class in spring?

This class is a wrapper around the Job that needs to be launched and around the JobParameters necessary to launch the Batch job. The following image illustrates the typical Spring Integration message flow in order to start a Batch job.


Video Answer


1 Answers

Found the issue. When using the @EnableAutoConfiguration annotation from Spring Boot it will invoke the org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration class which will initialize the database from the 'schema-mysql.sql' file.

Inside the schema-mysql.sql file is some code to reset the sequence id's for the batch meta tables which is why I was getting a duplicate key error:

INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0);
INSERT INTO BATCH_JOB_EXECUTION_SEQ values(0);
INSERT INTO BATCH_JOB_SEQ values(0);

The fix was to build the Spring Batch tables separately & then change the @EnableAutoConfiguration annotation to:

@EnableAutoConfiguration(exclude={BatchAutoConfiguration.class})

so that when the application starts up it will not try to initialize the Spring Batch tables.

To exclude or customize Spring Boot's auto-configuration for other components you can find some docs here: http://projects.spring.io/spring-boot/docs/spring-boot-autoconfigure/README.html

The BatchAutoConfiguration code: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java

like image 104
Mark Spangler Avatar answered Oct 15 '22 22:10

Mark Spangler