- 스프링 배치는 잡이 실행될 때 잡의 상태를 JobRepository에 저장해서 관리한다.
- 잡의 재시작 또는 아이템 재처리 시 어떤 동작을 수행할지 이 정보를 사용해 결정한다.
- 잡이 처리되는데 걸리는 시간, 오류로 인해 재시도된 아이템 수, 잡이 어디까지 실행됐는지 등을 기록하여 모니터링 데이터로 사용할 수 있다.
Job Repository란?
- 스프링 배치에서 JobRepository는 인터페이스 혹은 메타데이터를 저장하는 저장소를 의미한다. 여기서는 저장소에 대해 알아본다.
- 스프링배치에서는 관계형 데이터베이스와 인메모리 저장소를 사용할 수 있다.
관계형 데이터베이스 사용하기
- 스프링 배치가 제공하는 여러 테이블을 사용해 배치 메타데이터를 저장한다.

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가 사용하는 데이터소스는 명시적으로 구성해야 한다.
- 아니면 ChainedTransactionManager+LazyConnectionDataSourceProxy를 사용하여 두 개의 데이터소스를 묶어서 사용할 수 있다. 다음 글들을 참고하자.
- 예제와 설명은 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을 조회한다. |
'잡다구리' 카테고리의 다른 글
Redis Cluster 구성 테스트 (0) | 2022.08.12 |
---|---|
redis docker (0) | 2021.08.24 |
날짜와 시간 (0) | 2021.07.31 |
스프링 배치 완벽 가이드 - 6. 잡 실행하기 (0) | 2021.05.09 |
스프링 배치 완벽 가이드 - 4. 잡과 스텝 이해하기 (0) | 2021.05.02 |
스프링 배치 완벽 가이드 - 3. 예제 잡 애플리케이션 (0) | 2021.05.02 |
스프링 배치 완벽 가이드 - 2. 스프링 배치 (0) | 2021.05.01 |
7장 마이크로서비스 쿼리 구현 (Implementing queries in a microservice architecture) (0) | 2021.04.25 |
댓글