본문 바로가기
프로그래밍/Spring Batch

jobLauncher로 Job 실행시 runIdIncrementer가 작동하지 않는 경우

by 카프카뮈 2021. 10. 12.

이 글은 다음 상황에 대처한 경험을 작성한 글이다.

  • Spring Batch 사용 중, Job을 JobLauncher로 실행했을 때 실행되지 않는 경우를 확인했다.
  • runIdIncrementer가 작동하지 않았음을 확인하고, 이에 대한 조치를 수행했다.

Job의 중복 실행

Spring Batch의 경우, Job과 Step에 대한 실행 기록을 DB의 메타 테이블에 저장한다.

그리고 job 실행 시 해당 테이블을 조회하여,

만약 job의 parameter가 이전에 실행된 job의 parameter와 동일하다면 해당 job을 실행하지 않는다.

위처럼 결과값(ExitStatus)가 NOOP으로 나온다.

처음에는 왜 이런 번거로운 옵션이 있을까, 하고 난감했다.

하지만 다시 생각해 보니, 위 옵션을 통해 우리는 아래 상황을 방지할 수 있다.

  • 파라미터 별로 한 번만 수행되어야 하는 정산 배치 등이, 작업자 실수로 중복해서 실행된다면?
  • 날짜별로 실행해야 하는 기록 배치가, 잘못된 파라미터가 들어가 이전 기록을 덮어쓴다면?

물론 해당 옵션은 꽤 번거로운 옵션이므로, 무시하고 실행하는 등의 다른 옵션을 제공하기도 한다.

그 중 제일 간단한 옵션이, RunIdIncrementer를 parameter로 하는 실행이다.

RunIdIncrementer

RunIdIncrementer는 Job을 동일한 파라미터로 여러 번 반복 수행해야 할 때 사용되는 로직이다.

먼저 사용법부터 확인해 보자.

public Job job() {
    return JobBuilderFactory.get(JOB_NAME)
            .incrementer(new RunIdIncrementer())
            .start(step(null))
            .build();
}

위처럼 Job의 실행 구문 도중에 incrementer를 설정해 주고, RunIdIncrementer 객체를 생성해서 주입해준다.

이렇게 되면, 해당 runId가 Parameter로 추가되고, 이를 통해 반복 수행된 Job들을 구분하여 메타 테이블에 넣을 수 있다.

메타 테이블에도 runId가 목록으로 저장된다!

JobLauncher

Spring Batch를 실행시키는 방법은 다음과 같다.

  • 커맨드라인으로 실행
    • spring.batch.job.enabled = true로 설정한 뒤 프로젝트를 실행할 때 spring.batch.job.name을 직접 지정하는 경우 이 방식으로 실행된다.
  • JobLauncher로 실행

그 중 JobLauncher로 실행하는 경우는, 코드 내에서 능동적으로 원하는 타이밍에 배치를 실행할 수 있다.

나의 경우 Spring Scheduler를 활용해서 Job을 정해진 시간에 동작하도록 구현해 두었다.

아래의 코드를 보자. jobLauncher를 활용한 부분이 눈에 띈다.

@Component
@RequiredArgsConstructor
public class ChargerScheduler {
    private final ChargerInitConfiguration chargerInitConfiguration;
    private final ChargerUpdateConfiguration chargerUpdateConfiguration;
    private final JobLauncher jobLauncher;
    private final JobExplorer jobExplorer;

    @Scheduled(cron = "0 0/10 * * * *")
    public void runUpdateJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
        jobLauncher.run(chargerUpdateConfiguration.chargerUpdateJob(), new JobParameters());
    }
}
  • @Scheduled에 대한 설명은 추후 포스팅으로 대신하겠다.

그런데, 위의 코드대로 실행하면 한 가지 문제가 발생한다.

runIdIncrementer를 설정했는데도, 메타테이블에서 runId가 증가하지 않는다!!

심지어 위에 나온 커맨드라인으로 실행할 때에는 아무 문제가 없는데도 말이다.

 

이와 같은 문제가 생긴 이유는 간단하다.

Job이 커맨드라인으로 실행될 경우, JobLauncherCommandLineRunner가 실행을 담당하면서

JobParameters에 대한 설정을 해주고 있기 때문이다.

  • 이에 대해서는 좋은 포스팅이 있어, 링크로 대신한다.
 

JobLauncher사용시 RunIdIncrementer적용하기

batch 실행시 처리해주고 싶은 내용이 있어서 ApplicationRunner를 통해서 배치 job을 실행하려고 했다.spring.batch.job.enabled=false를 설정해서 배치가 자동으로 실행되지 않도록 한 뒤, ApplicationRunner를 직접

velog.io


해결하기

그렇다면 이제 해결을 해보자.

위의 jobLauncher를 사용한 코드를, 아래와 같이 수정했다.

@Component
@RequiredArgsConstructor
public class ChargerScheduler {
    private final ChargerInitConfiguration chargerInitConfiguration;
    private final ChargerUpdateConfiguration chargerUpdateConfiguration;
    private final JobLauncher jobLauncher;
    private final JobExplorer jobExplorer;

    @Scheduled(cron = "0 0/10 * * * *")
    public void runUpdateJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
        jobLauncher.run(
                chargerUpdateConfiguration.chargerUpdateJob(), new JobParametersBuilder(jobExplorer)
                        .getNextJobParameters(chargerUpdateConfiguration.chargerUpdateJob())
                        .toJobParameters()
        );
    }
}

변경된 부분이 눈에 띈다.

기존엔 jobLauncher.run의 두번째 인자를 new JobParameters()로 줬는데,

여기에서는 대신 Builder를 활용해서 Parameter를 직접 설정해주고 있다.

  • 주입된 JobExplorer를 빌더의 인자로 넣어주는데, 해당 값은 메타 테이블에 대한 read only 쿼리 기능을 위한 인터페이스이다.
    • 해당 값을 넣음으로써, runId에 대한 메타테이블 접근이 가능해진다.
  • getNextJobParameters에 Job을 넣어준다. 
    • 공식 문서에 따르면, Job의 상태를 바탕으로 JobParameters를 초기화한다고 한다.

위의 과정을 통해, 이제 runIdIncrementer를 사용할 준비를 마쳤다.

그리고 실행 결과, 문제 없이 job이 수행되는 것을 확인할 수 있었다.


마치며

여러 블로그와 문서의 도움을 받아 해결한 이슈였다.

하지만 한편으로는 고민도 된다. 내가 만든 batch는 데이터를 주기적으로 갱신하는 배치인 만큼 반복 실행되는게 맞지만, 그럼에도 날짜를 parameter로 주는 쪽이 더 바람직한가, 라는 생각도 들었고 말이다.

각각의 장단점이 있으리라 생각된다. runId를 쓰는 쪽이 훨씬 간편하지만, 대신 날짜를 parameter로 주면 메타 데이터를 분석하기 훨씬 깔끔하지 않을까?

이에 대한 재미있는 논의는, 우아한형제들 기술블로그의 포스팅을 참고.

 

덧붙여, runIdIncrementer를 Custom해서 활용하는 방법은 이동욱님의 블로그 포스팅을 참고 바란다.


위 문제의 경우, github에 issue로 남기고 PR도 진행했다.

해당 PR의 링크를 남긴다.

 

#42 Spring Batch의 Job Parameter 지정하기 by include42 · Pull Request #45 · include42/capstone-evinfo

Resolved: #42 변경 사항 Spring Scheduler에서 jobLauncher를 통해 job을 수행할 때, parameter 정보를 함께 전달하도록 수정함 올바르게 수행되는 것을 batch의 meta-table을 조회하여 확인함 특이 사항 관련 링크

github.com

 

잘못된 내용이 있다면 댓글 부탁드립니다. 감사합니다.

 

 

반응형

댓글