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
// ...