1. Spring Batch 롤백 제어 개요
- Spring Batch에서 Step 실행 중 예외가 발생하면 기본적으로 전체 트랜잭션이 롤백됩니다.
- 하지만 실무에서는 특정 예외에 대해서는 롤백을 방지해야 하는 경우가 있습니다.
- Spring Batch는 이러한 요구사항을 위해 롤백 제어 기능과 트랜잭션 Reader 처리 옵션을 제공합니다.
2. 기본 롤백 동작 방식
2.1 ItemWriter 예외 처리
- 기본적으로
ItemWriter
에서 발생하는 모든 예외는 Step의 트랜잭션 롤백을 발생시킵니다. - retry나 skip 설정과 관계없이 예외 발생 시 롤백이 수행됩니다.
- 이는 데이터 일관성을 보장하기 위한 안전장치 역할을 합니다.
2.2 ItemReader 예외 처리
- skip이 설정된 경우,
ItemReader
에서 발생하는 예외는 롤백을 발생시키지 않습니다. - 이는 Reader 단계에서의 오류가 전체 트랜잭션에 영향을 주지 않도록 하기 위함입니다.
롤백 동작의 이해
롤백은 데이터베이스의 ACID 특성을 보장하는 중요한 메커니즘입니다. Spring Batch에서는 청크 단위로 트랜잭션이 관리되며, 청크 내 어떤 항목에서든 예외가 발생하면 해당 청크 전체가 롤백됩니다.
3. 롤백 제어 설정 방법
3.1 noRollback 설정
- 특정 예외 타입에 대해 롤백을 방지하려면
noRollback()
메서드를 사용합니다. - 이는 예외가 발생해도 트랜잭션을 무효화하지 않는 경우에 유용합니다.
Java Configuration을 통한 설정
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("step1", jobRepository)
.<String, String>chunk(2, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.faultTolerant()
.noRollback(ValidationException.class)
.build();
}
이 설정을 통해 ValidationException
이 발생해도 트랜잭션이 롤백되지 않습니다.
3.2 여러 예외 타입 설정
- 여러 예외 타입에 대해 롤백을 방지하려면 각각의 예외 클래스를 지정할 수 있습니다.
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("step1", jobRepository)
.<String, String>chunk(2, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.faultTolerant()
.noRollback(ValidationException.class)
.noRollback(BusinessException.class)
.build();
}
4. 트랜잭션 Reader 처리
4.1 트랜잭션 Reader의 필요성
ItemReader
는 기본적으로 forward-only 계약을 가집니다.- Step은 롤백 시 재읽기를 피하기 위해 reader 입력을 버퍼링합니다.
- 하지만 JMS 큐와 같은 트랜잭션 리소스 기반 Reader의 경우 특별한 처리가 필요합니다.
JMS 큐와 트랜잭션
JMS 큐는 트랜잭션과 연결되어 있어, 롤백 시 큐에서 가져온 메시지들이 다시 큐로 돌아갑니다. 이런 경우 Step의 버퍼링 기능이 오히려 문제가 될 수 있습니다.
4.2 readerIsTransactionalQueue 설정
- 트랜잭션 리소스 기반 Reader의 경우
readerIsTransactionalQueue()
옵션을 사용합니다. - 이 설정은 Step이 아이템을 버퍼링하지 않도록 합니다.
트랜잭션 큐 Reader 설정
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("step1", jobRepository)
.<String, String>chunk(2, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.readerIsTransactionalQueue()
.build();
}
이 설정을 통해 JMS 큐와 같은 트랜잭션 리소스에서 안전하게 데이터를 읽을 수 있습니다.
5. 실무 적용 사례
5.1 검증 예외 처리 사례
- 데이터 검증 과정에서 발생하는 예외는 보통 롤백 대상이 아닙니다.
- 잘못된 데이터 포맷이나 비즈니스 룰 위반 등은 로그만 남기고 처리를 계속할 수 있습니다.
@Bean
public Step dataValidationStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("dataValidationStep", jobRepository)
.<CustomerData, ProcessedData>chunk(100, transactionManager)
.reader(customerDataReader())
.processor(validationProcessor())
.writer(processedDataWriter())
.faultTolerant()
.noRollback(DataValidationException.class)
.noRollback(FormatException.class)
.skip(DataValidationException.class)
.skipLimit(1000)
.build();
}
5.2 메시지 큐 처리 사례
- JMS나 RabbitMQ와 같은 메시지 큐에서 데이터를 읽는 경우의 설정입니다.
@Bean
public Step messageProcessingStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("messageProcessingStep", jobRepository)
.<Message, ProcessedMessage>chunk(50, transactionManager)
.reader(jmsMessageReader())
.processor(messageProcessor())
.writer(messageWriter())
.readerIsTransactionalQueue()
.faultTolerant()
.retry(ConnectionException.class)
.retryLimit(3)
.build();
}