본문 바로가기

Issue

[Spring Batch] Spring Batch chunk 지향 처리에서 error 없이 insert가 안되는 이슈

1. 이슈 환경

  • Spring Batch
  • chunk size = 1000
  • custom skip policy로 Duplicate Key 에러 발생 시 skip 하도록 처리

2. 해결

해당 배치는 A 테이블에서 B 테이블로 단순히 옮겨지는 배치였습니다. 단! B 테이블의 Primary Key (우선 키)가 A 테이블의 Primary Key의 일부였습니다.

- A 테이블 예시

  • id - PK
  • title
  • user_name
  • in_time - PK

- B 테이블 예시

  • id - PK
  • code
  • target

이러한 환경에서 에러는 나지 않고 정상적으로 실행되어 보였지만 DB를 확인해보니 insert가 전혀 되어 있지 않았습니다.

디버깅으로 확인해보니 Reader에서 A테이블을 Select 하는 부분에는 전혀 문제가 없었고 Processor도 문제가 없었습니다.

 

결국 해결방법은 Spring Batch Job에서 ItemWriter부분에 람다식을 사용하지 않으면서 해결되었습니다.

마지막 ItemWriter 부분에서 람다식을 사용하니 Writer 내부 코드에서 DuplicateKeyException을 try catch를 이용해 에러 핸들링을 하고 있어서 외부에서 skipPolicy에 걸리지 않고 있었습니다.

3. 코드

//Step 생성자
@Bean
@JobScope
public Step testStep(
	StepBuilderFactory stepBuilderFactory,
	ItemReader<ATableModel> itemReader,
	ItemProcessor<ATableModel, BTableModel> itemProcessor,
	ItemWriter<BTableModel> itemWriter,
	SkipPolicy duplicateKeySkipper,
	@Value("#{jobParameters['pageSize']}") int pageSize
) {
	return stepBuilderFactory.get("testStep")
		.<ATableModel, BTableModel>chunk(pageSize)
		.reader(itemReader)
		.processor(itemProcessor)
		.writer(itemWriter)
		.faultTolerant()
		.skipPolicy(duplicateKeySkipper)
		.build();
}

//Reader
@Bean
@StepScope
public ItemReader<ATableModel> itemReader(
	@Value("#{jobParameters['pageSize']}") int jobPageSize,
	ATableMapper aTableMapper
) {
	return CollectionPagingItemReader.<ATableModel>builder()
		.setPageSize(jobPageSize)
		.setCollectionReader((page, pageSize) -> aTableMapper.selectATable(page * pageSize, pageSize))
		.build();
}

//Processor
생략

//Writer
@Bean
public ItemWriter<BTableModel> itemWriter(BTableMapper bTableMapper) {
	//문제 부분
	return aTableModelList -> aTableModelList.forEach(bTableMapper::insertBTable);
}

=> 수정
@Bean
public ItemWriter<BTableModel> itemWriter(BTableMapper bTableMapper) {
	JdbcBatchItemWriter<BTableModel> itemWriter = new JdbcBatchItemWriter<>();
    itemWriter.setSql("...");
    
    return itemWriter;
}
@Slf4j
public class DuplicateKeySkipper implements SkipPolicy {
	@Override
	public boolean shouldSkip(Throwable throwable, int skipCount) throws SkipLimitExceededException {
		if (throwable instanceof DuplicateKeyException) {
			if (skipCount >= 0) {
				LOGGER.warn("skip!");
			}
			return true;
		}
		return false;
	}
}

이슈에 대한 더 좋은 해결방법 혹은 질문이나 오류 지적

언제나 환영하고 있습니다 ㅎㅎ

반응형