2023년 05월 26일 작성
Job
과 Step
의 실행 상태, 파라미터, 컨텍스트 등을 관리하기 위해 메타데이터 테이블을 필요로 한다. [1]메타데이터 엔티티들은 JobInstance
, JobExecution
, JobParameters
, StepExecution
등이 있고, 이들을 관리하는 테이블들은 총 6개로 다음과 같다.
BATCH_JOB_INSTANCE
: Job이 실행될 때마다 생성된 JobInstanceBATCH_JOB_EXECUTION
: JobInstance의 시작/종료 시간, 상태, 종료 메세지 등 정보BATCH_JOB_EXECUTION_PARAMS
: JobExecution에 전달되는 JobParametersBATCH_JOB_EXECUTION_CONTEXT
: Job 범위에서 관리하는 컨텍스트가 serialize되어 기록됨.BATCH_STEP_EXECUTION
: Job 하위 Step들의 시작/종료 시간, 상태, read/write/skip/commit 카운트 등 상세한 정보BATCH_STEP_EXECUTION_CONTEXT
: Step 범위에서 관리하는 컨텍스트가 serialize되어 기록됨.
(이미지 출처 : https://docs.spring.io/spring-batch/docs/current/reference/html/schema-appendix.html)
문제 상황
혹은 비주류 DB에 메타데이터를 저장하려 할 경우, 스프링 배치에서 지원하지 않을 수 있다. [2] (ex. Tibero)
웹에서 찾아보면 주로 다음 솔루션이 보인다.
DefaultBatchConfigurer
를 구현하고 DataSource를 설정하는 부분을 비워두는 방법
DataSource가 설정되지 않을 때, 스프링 배치는 MapJobRepositoryFactoryBean
을 이용해 메타데이터를 Map 기반으로 인메모리 관리한다.
또한 Spring Batch 5.0에서는 DefaultBatchConfigurer가 제거되어 이 방법을 사용할 수 없는데,
@EnableBatchProcessing
어노테이션은 5.0 버전에서 몇가지 변경점이 있다.
Configuration에서 DataSource 빈을 두 개 등록해주고 서비스 데이터가 들어갈 데이터소스에 @Primary
어노테이션을 설정해두었다.
TransactionManager 빈을 등록할 때 사용할 데이터소스를 지정해주었으며,
@EnableBatchProcessing(dataSourceRef = "batchDataSource", transactionManagerRef = "jpaTransactionManager")
어노테이션을 통해 두 데이터소스를 분리 지정했다.EmbeddedDataSource
로 구성하여 H2와 같은 인메모리 DB를 사용할 수 있다.전체 코드는 다음과 같다.
config/BatchConfig.java
@Configuration
public class BatchConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource-batch.url}")
private String batchUrl;
@Value("${spring.datasource-batch.username}")
private String batchUsername;
@Value("${spring.datasource-batch.password}")
private String batchPassword;
@Value("${spring.datasource-batch.driver-class-name}")
private String batchDriverClassName;
@Bean
@Primary
public DataSource dataSource() {
return DataSourceBuilder.create()
.url(url)
.username(username)
.password(password)
.driverClassName(driverClassName)
.build();
}
@Bean
public DataSource batchDataSource() {
return DataSourceBuilder.create()
.url(batchUrl)
.username(batchUsername)
.password(batchPassword)
.driverClassName(batchDriverClassName)
.build();
}
@Bean
public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory);
jpaTransactionManager.setDataSource(dataSource());
return jpaTransactionManager;
}
}
JobConfig
@Configuration
@RequiredArgsConstructor
@EnableBatchProcessing(dataSourceRef = "batchDataSource", transactionManagerRef = "jpaTransactionManager")
public class DailyAggregationJobConfig {
private final static String jobName = "dailyAggregationJob";
private final JobRepository jobRepository;
private final PlatformTransactionManager jpaTransactionManager;
private final EntityManagerFactory entityManagerFactory;
@Bean
@Qualifier(jobName)
public Job dailyAggregationJob() {
return new JobBuilder(jobName, jobRepository)
.start(dailyAggregationStep1())
.build();
}
@Bean
@JobScope
public Step dailyAggregationStep1() {
return new StepBuilder(jobName + "Step1", jobRepository)
.<DailyAggregationVo, DailyAggregatedData>chunk(1000, jpaTransactionManager)
.reader(dailyAggregationItemReader())
.processor(dailyAggregationItemProcessor())
.writer(dailyAggregationItemWriter())
.build();
}
@Bean
@StepScope
public DailyAggregationItemReader dailyAggregationItemReader() {
return new DailyAggregationItemReader(entityManagerFactory);
}
@Bean
@StepScope
public DailyAggregationItemReader dailyAggregationItemProcessor() {
return new DailyAggregationItemReader();
}
@Bean
@StepScope
public DailyAggregationItemReader dailyAggregationItemWriter() {
return new DailyAggregationItemReader(entityManagerFactory);
}
}
resources/application.yaml
spring:
// ...
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:20000/playground?characterEncoding=UTF-8&serverTimezone=Asia/Seoul
username: username
password: password
datasource-batch:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:20001/batch_metadata?characterEncoding=UTF-8&serverTimezone=Asia/Seoul
username: username
password: password
// ...