Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a bad idea to change the Spring Batch Meta-Data tables manually?

Background

I'm using Spring Batch 2.1.8, and run jobs by CommandLineJobRunner. Such as:

java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:launchContext.xml theJobId

Problem

At some condition such as a server crash, running job could be interrupted. But the interrupted job left a STARTED status in the Spring Batch Meta-Data tables, and can't be run again.

org.springframework.batch.core.repository.JobExecutionAlreadyRunningException: A job execution for this job is already running

I can think of two solutions:

Solution1

Add a new job parameter and change it everytime to make it a "new" job for Spring Batch. Such as:

java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:launchContext.xml theJobId times=0

And when need to rerun it, do clear all corresponding output data, count up times once, and then rerun the job.

Solution2

Change the Spring Batch Meta-Data tables manually.

To update the status to make the job restartable. Such as:

UPDATE BATCH_JOB_EXECUTION SET END_TIME = SYSTIMESTAMP, STATUS = 'FAILED', EXIT_CODE = 'FAILOVER' WHERE JOB_EXECUTION_ID =
    (SELECT MAX(JOB_EXECUTION_ID) FROM BATCH_JOB_EXECUTION WHERE JOB_INSTANCE_ID =
        (SELECT MAX(JOB_INSTANCE_ID) FROM BATCH_JOB_INSTANCE WHERE JOB_NAME = 'XXX'));

I've tried it and it seems works well.

Question

Is Solution2 a bad idea? Are there any traps?

Thanks in advance. And any other solutions are appreciated.

like image 855
songyuanyao Avatar asked Apr 01 '14 01:04

songyuanyao


People also ask

Will Spring Batch creates it own metadata tables?

The meta table's scripts are stored in the spring-batch. jar , you need to create it manually. Run your Spring batch jobs again, those meta tables will be created automatically.

What is Spring Batch metadata tables?

Overview. The Spring Batch Metadata tables closely match the Domain objects that represent them in Java. For example, JobInstance , JobExecution , JobParameters , and StepExecution map to BATCH_JOB_INSTANCE , BATCH_JOB_EXECUTION , BATCH_JOB_EXECUTION_PARAMS , and BATCH_STEP_EXECUTION , respectively.

Why does spring batch need database?

Spring Batch by default uses a database to store metadata on the configured batch jobs. In this example, we will run Spring Batch without a database. Instead, an in-memory Map based repository is used.

What is ExecutionContext in Spring Batch?

An ExecutionContext is a set of key-value pairs containing information that is scoped to either StepExecution or JobExecution . Spring Batch persists the ExecutionContext , which helps in cases where you want to restart a batch run (e.g., when a fatal error has occurred, etc.).


2 Answers

Solution 2 is the accepted approach right now. The API does not provide a way to fix this scenario. There have been requests in the past for the framework to clean up automatically, but 99% of the time, a human decision is needed to determine if cleanup is truly required.

My only note for option 2 would be to check the BATCH_STEP_EXECUTION table as well to see what state the last executed step was left in.

like image 176
Michael Minella Avatar answered Oct 11 '22 17:10

Michael Minella


I created a specific spring bean for this which is triggered on a container refresh (which happens on app (re)start too).

It searches for 'running' jobs, marks them 'FAILED' and restarts them.

import java.util.Date;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class BatchJobRestarter implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOGGER  = LoggerFactory.getLogger(BatchJobRestarter.class);

    @Autowired
    private JobExplorer         jobExplorer;

    @Autowired
    JobRepository               jobRepository;

    @Autowired
    JobOperator                 jobOperator;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        LOGGER.info("Container restart: restarting 'running' batch jobs");
        List<String> jobs = jobExplorer.getJobNames();
        for (String job : jobs) {
            Set<JobExecution> runningJobs = jobExplorer.findRunningJobExecutions(job);

            for (JobExecution runningJob : runningJobs) {
                try {
                    LOGGER.info("Restarting job {} with parameters {}", runningJob.getJobInstance().getJobName(), runningJob.getJobParameters().toString());
                    runningJob.setStatus(BatchStatus.FAILED);
                    runningJob.setEndTime(new Date());
                    jobRepository.update(runningJob);
                    jobOperator.restart(runningJob.getId());
                } catch (Exception e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
    }
}

Steef

like image 44
Steef Avatar answered Oct 11 '22 15:10

Steef