본문 바로가기
잡다구리

스프링 배치 완벽 가이드 - 5. JobRepository와 메타데이터

by Growing! 2021. 5. 6.
  • 스프링 배치는 잡이 실행될 때 잡의 상태를 JobRepository에 저장해서 관리한다.
  • 잡의 재시작 또는 아이템 재처리 시 어떤 동작을 수행할지 이 정보를 사용해 결정한다.
  • 잡이 처리되는데 걸리는 시간, 오류로 인해 재시도된 아이템 수, 잡이 어디까지 실행됐는지 등을 기록하여 모니터링 데이터로 사용할 수 있다.

Job Repository란?

  • 스프링 배치에서 JobRepository는 인터페이스 혹은 메타데이터를 저장하는 저장소를 의미한다. 여기서는 저장소에 대해 알아본다.
  • 스프링배치에서는 관계형 데이터베이스와 인메모리 저장소를 사용할 수 있다. 

관계형 데이터베이스 사용하기

  • 스프링 배치가 제공하는 여러 테이블을 사용해 배치 메타데이터를 저장한다.

Spring Batch Metadata Schema

Version

  • 대부분의 메타데이터 테이블에서 VERSION 컬럼을 가지고 있다.
  • 스프링 배치는 데이터베이스에 업데이트를 처리할 때 낙관적 잠금 전략(Optimistic Locking Strategy)을 사용한다. 업데이트가 발생하면 VERSION 컬럼의 값을 1씩 증가시킨다. 만일 읽어왔던 데이터를 업데이트하려는데, 이미 VERSION 값이 달라져 있다면 동시 업데이트가 발생한 것이므로 OptimisticLockingFailureException을 던진다.

BATCH_JOB_INSTANCE

  • 잡을 식별 가능한 파라미터로 실행하면 JobInstance 가 생성된다. 이 테이블의 레코드는 JobInstance를 나타낸다.
  • 잡의 논리적인 실행을 나타낸다.
컬럼 타입 설명
JOB_INSTANCE_ID BIGINT PK
VERSION BIGINT 버전
JOB_NAME VARCHAR(100) 잡 이름
JOB_KEY VARCHAR(32) 잡 파라미터들을 직렬화(해시)한 값.
잡 파라미터는 동일한 잡에서 JobInstance를 식별하는데 사용된다.

BATCH_JOB_EXECUTION

  • 잡의 물리적인 실행을 나타낸다. 동일한 파라미터로 잡을 여러 번 실행하면 여러 개의 레코드가 생성된다.
  • JobInstance(BATCH_JOB_EXECUTION)과는 1:N 관계 이다.
컬럼 타입 설명
JOB_EXECUTION_ID BIGINT PK
VERSION BIGINT 버전
JOB_INSTANCE_ID BIGINT BATCH_JOB_INSTANCE 테이블의 PK
CREATE_TIME DATETIME(6) execution의 생성 시각
START_TIME DATETIME(6) execution의 시작 시각
END_TIME DATETIME(6) execution의 종료 시각
STATUS VARCHAR(10) execution의 상태
EXIT_CODE VARCHAR(2500) execution의 exit code
EXIT_MESSAGE VARCHAR(2500) exit에 대한 자세한 메시지. 실패한 경우라면 스택 트레이스.
LAST_UPDATED DATETIME(6) 최종 갱신 시각

BATCH_JOB_EXECUTION_CONTEXT

  • JobExecution의 ExecutionContext와 관련된 모든 정보를 저장한다.
  • JobExecution(BATCH_JOB_EXECUTION)과는 1:1 관계이다.
  • 일반적으로 상태를 저장하며, 실패한 잡을 다시 실행하면 이 데이터를 읽어서 "실패한 곳에서 부터 다시 시작"할 수 있다.
컬럼 타입 설명
JOB_EXECUTION_ID BIGINT BATCH_JOB_EXECUTION 테이블의 PK
SHORT_CONTEXT VARCHAR(2500) SERIALIZED_CONTEXT의 문자열 버전
SERIALIZED_CONTEXT TEXT 직렬화(Jackson2)된 전체 context

BATCH_JOB_EXECUTION_PARAMS

  • 잡이 실행될 때 마다 잡 파라미터를 저장한다.
  • JobExecution(BATCH_JOB_EXECUTION) 과는 1:N 관계이다.
  • 잡을 식별하는데 사용되는 파라미터는 IDENTIFYING 플래그가 Y이다.
  • 테이블은 key-value 정보가 저장되며, 데이터 타입별로 테이블이 분리되어 있지 않고 하나의 테이블에 여러 데이터 타입을 저장할 수 있도록 반정규화(demormalized) 되어 있다.
컬럼 타입 설명
JOB_EXECUTION_ID BIGINT BATCH_JOB_EXECUTION의 PK
TYPE_CODE VARCHAR(6) 저장된 값의 데이터 타입("STRING", "DATE", "LONG", "DOUBLE")
KEY_NAME VARCHAR(100) 파라미터 이름
STRING_VAL VARCHAR(250) 타입이 string인 경우의 파라미터 값
DATE_VAL DATETIME(6) 타입이 date인 경우의 파라미터 값
LONG_VAL BIGINT 타입이 long인 경우의 파라미터 값
DOUBLE_VAL DOUBLE 타입이 double인 경우의 파라미터 값
IDENTIFYING CHAR(1) 파라미터가 JobInstance를 식별하는데 사용되는지 여부

BATCH_STEP_EXECUTION

  • 스텝의 실행을 나타낸다.
  • 스텝의 시작/종료 시각, 상태를 저장한다.
  • 스텝의 실행에 대한 분석을 위한 다양한 횟수(read/process/write/skip/rollbak/...) 값도 저장한다.
컬럼 타입 설명
STEP_EXECUTION_ID BIGINT PK
VERSION BIGINT 버전
STEP_NAME VARCHAR(100) 스텝 이름
JOB_EXECUTION_ID BIGINT BATCH_JOB_EXECUTION 테이블의 PK
START_TIME DATETIME(6) execution의 시작 시각
END_TIME DATETIME(6) execution의 종료 시각
STATUS VARCHAR(10) execution의 상태
COMMIT_COUNT BIGINT 트랜잭션을 커밋한 횟수
READ_COUNT BIGINT 읽은 아이템의 수
FILTER_COUNT BIGINT 필터링(제외된)된 아이템의 수
WRITE_COUNT BIGINT 기록된 아이템 수
READ_SKIP_COUNT BIGINT ItemReader 내에서 예외가 발생해서 건너뛴 아이템 수
PROCESS_SKIP_COUNT BIGINT ItemProcessor 내에서 예외가 발생해서 건너뛴 아이템 수
WRITE_SKIP_COUNT BIGINT ItemWriter 내에서 예외가 발생해서 건너뛴 아이템 수
ROLLBACK_COUNT BIGINT 롤백된 트랜잭션 수(retry를 하기 위한 롤백이나 skip을 처리하기 위한 롤백도 포함)
EXIT_CODE VARCHAR(2500) execution의 exit code
EXIT_MESSAGE VARCHAR(2500) exit에 대한 자세한 메시지. 실패한 경우라면 스택 트레이스.
LAST_UPDATED DATETIME(6) 최종 갱신 시각

BATCH_STEP_EXECUTION_CONTEXT

  • StepExecution의 ExecutionContext와 관련된 모든 정보를 저장한다.
  • StepExecution(BATCH_STEP_EXECUTION)과는 1:1 관계이다.
  • 일반적으로 상태를 저장하며, 실패한 잡을 다시 실행하면 이 데이터를 읽어서 "실패한 곳에서 부터 다시 시작"할 수 있다.
컬럼 타입 설명
STEP_EXECUTION_ID BIGINT BATCH_STEP_EXECUTION 테이블의 PK
SHORT_CONTEXT VARCHAR(2500) SERIALIZED_CONTEXT의 문자열 버전
SERIALIZED_CONTEXT TEXT 직렬화(Jackson2)된 전체 context

인메모리 Job Repository

  • 스프링 배치는 개발이나 테스트 환경에서 사용할 수 있는 인메모리 JobRepository를 제공한다.
  • java.util.Map 객체를 저장소로 사용하는 JobRepository 구현체를 제공한다.

배치 인프라스트럭처 구성하기

  • @EnableBatchProcessing 애너테이션을 사용하면 대부분의 배치 인프라스트럭처가 자동으로 준비된다.
  • BatchConfigurer 인터페이스를 사용해서 스프링 배치 인프라스트럭처를 커스터마이징 할 수 있다.

BatchConfigurer 인터페이스

  • BatchConfigurer 인터페이스는 스프링 배치 인프라스트럭처 컴포넌트의 구성을 커스터마이징하는데 사용되는 전략 인터페이스이다.
  • @EnableBatchProcessing 애너테이션을 사용하면, SimpleBatchConfiguration에서 인프라스트럭처 컴포넌트들을 생성하여 ApplicationContext에 등록한다. 커스터마이징한 컴포넌트 생성해서 bean으로 등록하고 싶다면 BatchConfigurer를 사용하면 된다.
/**
 * Strategy interface for users to provide as a factory for custom components needed by a Batch system.
 */
public interface BatchConfigurer {

    JobRepository getJobRepository() throws Exception;

    PlatformTransactionManager getTransactionManager() throws Exception;

    JobLauncher getJobLauncher() throws Exception;

    JobExplorer getJobExplorer() throws Exception;
}

 

  • 각 메서드는 스프링 배치 인프라스트럭처의 주요 컴포넌트를 제공한다.
  • PlatformTransactionManager는 프레임워크가 제공하는 모든 트랜잭션 관리에서 사용된다.
  • JobExplorer는 job repository의 데이터에 읽기 전용으로 접근할 수 있다.
  • 스프링 배치가 제공하는 구현체인 DefaultBatchConfigurer는 모든 컴포넌트에 대한 기본 구현을 제공하므로, 이를 상속해 필요한 메소드만 재정의하면 된다.
    • 오버라이드를 위한 createJobLauncher(), createJobRepository(), createJobExplorer() 메소드를 제공한다.

JobRepository 커스터마이징하기

  • JobRepository는 JobRepositoryFactoryBean을 통해 생성되며, 이 FactoryBean은 다양한 속성을 커스터마이징 할 수 있는 기능을 제공한다 (p.190 표 5-7)
  • createJobRepository() 메소드를 재정의해야 하는 가장 일반적인 시나리오는 ApplicationContext에 두 개 이상의 데이터소스가 존재하는 경우이다. 이 경우 JobRepository가 사용하는 데이터소스는 명시적으로 구성해야 한다.
  • 예제와 설명은 p.190~192를 참고하자.

TransactionManager 커스터마이징하기

  • TransactionManager를 커스터마이징 하려면 오버라이드용 메서드가 아니라 getTransactionManager():TransactionManager 를 사용하여야 한다.
  • 왜냐하면 DefaultBatchConfigurer는 setDataSource(DataSource) 메서드에서 내부적으로 getTransactionManager()를 호출하여 null인 경우에는 DataSourceTransactionManager를 자동으로 생성하기 때문이다.
  • 그래서 DefaultBatchConfigurer는 오버라이드용 메소드인 createTransactionManager()는 제공하지 않는다.
@Component
public class DefaultBatchConfigurer implements BatchConfigurer {

    private PlatformTransactionManager transactionManager;

    @Autowired(required = false)
    public void setDataSource(DataSource dataSource) {
        ...
        if(getTransactionManager() == null) {
            logger.warn("No transaction manager was provided, using a DataSourceTransactionManager");
            this.transactionManager = new DataSourceTransactionManager(this.dataSource);
        }
    }
    ...

JobExplorer 커스터마이징하기

  • JobExplorer는 JobRepository가 다루는 데이터에 대한 읽기 전용 뷰이다.
  • JobRepository를 커스터마이징 했다면, JobExeplorer도 동일하게 커스터마이징 하는게 좋다.
  • 예제와 설명은 p.194~195를 참고하자.

JobLauncher 커스터마이징하기

  • JobLauncher는 스프링 배치 잡을 실행하는 진입점이다.
  • 스프링 부트는 SimpleJobLauncher를 사용하며, 스프링 부트로 잡을 실행할 때는 커스터마이징할 필요가 없다.
  • 스프링 MVC의 컨트롤러를 통해 잡을 실행하려는 경우 SimpleLauncher의 동작 방식을 조정할 수 있다.

데이터베이스 구성하기

  • 스프링 부트에서 데이터베이스를 사용하려면 데이터베이스 드라이버를 클래스 패스에 추가하고, 적절한 데이터소스 프로퍼티를 구성하면 된다 - spring.datasource.*
  • 스프링 배치의 메타데이터 스키마를 생성하기 위한 스크립트의 실행하도록 지정할 수 있다 - spring.batch.initialize-schema
    • always: 애플리케이션 실행시 마다 스크립트를 실행. DROP문이 없으며 오류가 발생하면 무시.
    • never: 스크립트를 실행하지 않음.
    • embedded (default): 내장 데이터베이스를 사용. 애플리케이션 실행 시마다 데이터가 초기화됨.
# application.yml
spring:
    datasource:
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/spring_batch
        username: 'spring_batch'
        password: 'spring_batch'
    batch:
        initialize-schema: always

잡 메타데이터 사용하기

JobExplorer

  • 스프링 배치는 메타데이터 테이블에 쉽게 접근할 수 있도록 JobExeplorer를 제공한다.
  • JobExplorer는 읽기 전용으로 JobRepository의 데이터에 접근하는 기능을 제공한다.
  • 잡 인스턴스와 실행에 대한 정보를 조회할 수 있는 메서드들을 제공한다.
  • 예제는 p.199~202를 참고한다.
메서드 설명
findRunningJobExecutions(jobNam:String): Set<JobExecution> 잡 이름으로 실행중인 JobExecution들을 조회한다.
findJobInstancesByJobName(jobName:String, start:int, count:int): List<JobInstance> 잡 이름에 대한 JobInstance 목록을 조회한다(구간 조회).
getJobExecution(executionId:Long): JobExecution JobExecution을 조회한다.
getJobExecutions(jobInstance:JobInstance): List<JobExecution> JobInstance와 관련된 JobExecution 목록을 조회한다.
getJobIntance(instanceId:Long): JobInstance JobInstance를 조회한다.
getJobInstances(jobName:String, start:int, count:int): List<JobInstance> 잡 이름에 대한 JobInstance 목록을 조회한다(구간 조회).
getJobInstanceCount(jobName:String): int 잡 이름으로 생성된 JobInstance 개수를 조회한다.
getJobNames(): List<String> 모든 잡 이름을 조회한다.
getStepExecution(jobExecutionId:Long, stepExecutionId:Long): StepExecution jobExecutionId와 stepExecutionId를 가진 StepExecution을 조회한다.

 

댓글