Это плохая идея, чтобы изменить таблицы метаданных Spring Batch вручную?

Фон

Я использую Spring Batch 2.1.8 и запускаю задания по CommandLineJobRunner, Такие как:

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

проблема

При некоторых условиях, таких как сбой сервера, выполнение задания может быть прервано. Но прерванная работа оставила STARTED статус в таблицах метаданных Spring Batch и не может быть запущен снова.

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

Я могу придумать два решения:

Solution1

Добавьте новый параметр задания и меняйте его каждый раз, чтобы сделать его "новым" заданием для Spring Batch. Такие как:

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

А когда нужно его перезапустить, очистите все соответствующие выходные данные, подсчитайте times один раз, а затем снова запустите работу.

Solution2

Измените таблицы метаданных Spring Batch вручную.

Чтобы обновить статус, чтобы сделать работу перезапускаемой. Такие как:

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'));

Я пробовал, и, кажется, работает хорошо.

Вопрос

Solution2 плохая идея? Есть ли ловушки?

Заранее спасибо. И любые другие решения приветствуются.

2 ответа

Решение

Решение 2 является принятым подходом прямо сейчас. API не предоставляет способ исправить этот сценарий. В прошлом были запросы на автоматическую очистку платформы, но в 99% случаев необходимо человеческое решение, чтобы определить, действительно ли требуется очистка.

Мое единственное примечание для варианта 2 - это проверить таблицу BATCH_STEP_EXECUTION, чтобы увидеть, в каком состоянии был оставлен последний выполненный шаг.

Для этого я создал специальный пружинный компонент, который запускается при обновлении контейнера (что также происходит при запуске приложения).

Он ищет "запущенные" задания, помечает их как "СБОЙ" и перезапускает.

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

Другие вопросы по тегам