사용자 도구

사이트 도구


springframework:batch

Spring Framework Batch

@EnableBatchProcessing

DefaultBatchConfigurer

  • 설정 중 일부를 Override하고자 한다면 DefaultBatchConfigurer 를 Configuration class에서 상속해서 메소드를 오버라이드 한다.
  • dataSource 오버라이드 예
    // 다른 configuration class 에서 DataSource 생성 - embedded 예제
    @Bean
    public DataSource batchDataSource() {
        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:/org/springframework/batch/core/schema-h2.sql")
                .build();
    }
     
    @EnableBatchProcessing
    @Configuration
    public class BatchApplication extends DefaultBatchConfigurer {
        // dataSource 설정부분 override. transactionManager도 자동으로 생성함.
        @Autowired
        @Override
        public void setDataSource(@Qualifier("batchDataSource") DataSource batchDataSource) {
            super.setDataSource(batchDataSource);
        }
    }
  • dataSource 자체를 null로 지정하면 MapJobRepository로 DB없이 작동하게 만들어진다.
  • SpringBoot 에서 DefaultBatchConfigurer 사용시 TransactionManager 오작동 문제가 발생한다. SpringBoot와 SpringBatch 참고

JobRepository

  • org.springframework.batch.core.repository.support.JobRepositoryFactoryBeanSimpleJobRepository 생성
  • MapJobRepositoryFactoryBean 로 in memory Job Repository 생성 가능.
  • org.springframework.batch.core 패키지에 schema-*.sql 파일이 Batch Job Repository DB 스키마이다.
  • schema-mysql.sql의 경우
    • textlongtext
    • datetimedatetime(6)로 변경해서 실행할 것.
    • 테이블 생성시 명백하게 대문자로 테이블명을 생성해줘야한다.
    • 만약 MySQL을 사용하면서 소문자로 테이블을 생성해버렸다면 다음과 같은 설정을 추가해줘야한다.
      • see MySQL 설정 대소문자 테이블명 부분의 lower_case_table_names=1

Migration

Transaction 설정

Connection Pool 설정

  • batch job 이 job 단위로 단독 프로세스로 실행되는 경우
  • batch job 은 실제로 Parallel 로 실행되는게 아니면 DB 커넥션이 1~2개 정도 밖에 필요하지 않다.
  • parallel, async 실행에 대비해서 maximum connection 갯수는 충분한 숫자로 하되,
  • minimum 갯수를 5개 혹은 그 이하로 지정해야 Batch job 이 실행 될 때마다 커넥션을 과도하게 맺느라 들어가는 실행시간 지연 문제를 해소하고, 불필요하게 DB connection 을 점유하여 DB 자체의 한계에 봉착하는 문제를 피할 수 있다.

트랜잭션 매니저를 지정하지 않으면

TransactionManager를 지정하지 않으면 다음과 같은 오류가 발생한다.

java.lang.IllegalStateException: To use the default BatchConfigurer the context must contain precisely one DataSource, found X

트랜잭션 기본 설정 및 트랜잭션 없이 실행하기

TransactionManager 를 ResourcelessTransactionManager 로 지정하면 된다.

혹은 아래와 같이 propagation=“NEVER”로 지정한다.

<batch:job id="jobid">
    <batch:step id="step1">
        <batch:tasklet ref="taskletId" transaction-manager="transactionManager">
            <batch:transaction-attributes propagation="NEVER"/>
        </batch:tasklet>
    </batch:step>
</batch:job>

Java Config

  • Step 등을 생성할 때 @Value(“#{jobParameter[paramName]}” String param 형태로 Job Parameter를 받을 수 있다.
@Bean
@JobScope
public Step myStep(@Value("#{jobParameters[date]}" String date, 
    @Value("#{jobParameters[count]}") long count) {
    // ...
}

Chunk 사이즈가 크고 Transaction 이 긴데, 외부에 write 대상건을 알려주는 경우 DB Lock 발생할 수 있음

  • batch 에 트랜잭션 매니저를 설정하고, writer 부분에서 데이터를 insert/update 할 때마다 그 사실을 MQ 등으로 외부에 알려주면,
  • 외부에서 그 데이터를 사용할 수 있게 하고자 했다면,
  • 절대로 Step 에 트랜잭션 매니저를 지정하지 말고, ResourcelessTransactionManager 를 사용하거나 CHUNK 를 작게 가져가야 한다.
  • ResourcelessTransactionManager 로 Batch Job 을 지정하면, Batch job 의 chunk 가 아닌, 각각의 Service/Repository 등의 메소드에 설정된 @Transactional을 기준으로 트랜잭션이 작동하게 된다.
  • 안그러면 CHUNK size 만큼의 처리가 모두 끝날때까지 DB lock 이 잡혀있어서 외부에서 이벤트를 받아서 처리하는 측에서 lock 획득에 실패해서 오류가 발생하게 된다.
  • 이게 아니더라도, 트랜잭션은 항상 짧게 가져가야 한다.

JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters 오류 발생

jobBuilderFactory.get("myJobName")
            .start(step())
            . .....
            .incrementer(new RunIdIncrementer())
            .build();

RunIdIncrementer 사용시 기존 파라미터가 현재 파라미터 지정한 것을 덮어씀

/**
 * 파라미터를 복사하지 않는 RunIdIncrementer
 */
public class ParamCleanRunIdIncrementer implements JobParametersIncrementer {
    private static String RUN_ID_KEY = "run.id";
    private String key = RUN_ID_KEY;
 
    public void setKey(String key) { this.key = key; }
 
    @Override
    public JobParameters getNext(JobParameters parameters) {
        JobParameters params = (parameters == null) ? new JobParameters() : parameters;
        long id = params.getLong(key, 0L) + 1;
        return new JobParametersBuilder().addLong(key, id).toJobParameters(); // 이부분이 RunIdIncrementer와 다르다.
    }
}

Java Config Spring Bean 설정시 리턴 타입을 인터페이스가 아닌 구현 클래스로 지정할 것.

  • @StepScope 등을 사용하는 등 할 경우 리턴 타입을 ItemReader<X>, ItemWriter<Y> 처럼 인터페이스로 할 경우 프록시 객체가 생성되고 그로 인해서 instanceOf 를 사용하는 일부 코드들이 작동하지 않거나, annotation 기반의 listener 등이 동작하지 않는 문제를 일으킨다.
  • 따라서 Spring Batch 용 각 스텝별 Bean 을 생성해서 리턴할 때는 항상 구현 클래스를 리턴 타입으로 지정할 것.
  • 다음과 같은 WARN 을 볼 수도 있다.
o.s.b.c.l.AbstractListenerFactoryBean : org.springframework.batch.item.ItemReader is an interface. The implementing class will not be queried for annotation based listener configurations. If using @StepScope on a @Bean method, be sure to return the implementing class so listner annotations can be used.
org.springframework.batch.item.ItemReader는 인터페이스입니다. 구현 클래스는 어노테이션 기반 listener 구성에 대해 실행되지 않습니다. @Bean 메소드에서 @StepScope를 사용하는 경우 listner 어노테이션을 사용할 수 있도록 구현 클래스를 리턴해야합니다.

Test

참고

springframework/batch.txt · 마지막으로 수정됨: 2023/12/08 13:43 저자 kwon37xi