1. Step Skip
Step 처리 도중에서 설정한 Exception이 발생하게 되면 해당 데이터를 건너뛰는 기능입니다.
데이터의 작은 오류에 대해서 Step 의 실패 처리 대신 Skip 을 함으로써 배치 수행의 빈번한 실패를 줄일 수 있습니다.
Skip 제한을 초과하는 경우가 되면 Exception이 발생됩니다.

ItemReader
item 을 읽던 도중 예외가 발생하게 되면 해당 item 을 skip 하고 다음 item 을 읽습는다.
ItemProcessor
item 을 처리하던 도중 예외가 발생하면 해당 Chunk 의 첫 단계로 돌아가서 itemReader로부터 다시 데이터를 전달받습니다.
itemProcessor 는 다시 item 을 받아 실행하는데 이전 실행에서 발생한 예외 정보가 내부적으로 남아있어 위의 그림처럼 item2 의 차례가 온다면 처리하지 않고 넘어갑니다.
itemWriter
writer 에서 예외가 발생하게 되면 다시 Chunk 단위로 ItemReader 로 돌아갑니다.
write 작업 실패시 데이터 처리를 위해 재동하게 되며, ItemProcessor 는 ItemWriter 로 리스트가 아니라 한 건씩만 보내서 처리합니다.
2. Step Retry
Step 처리 도중에서 설정한 Exception이 발생하게 되면 재시도 기능입니다.
Skip 과 다르게 ItemReader 에서는 지원하지 않으며, ItemProcessor, ItemWriter 에서만 Retry 가 가능합니다.
Retry Count 는 Item 마다 각각 존재합니다.
ItemProcessor
예외가 발생한다면 다시 Chunk 처음부터 처리합니다.
itemWriter
writer에서 예외가 발생하게 되면 다시 Chunk 단위로 ItemReader 로 돌아가 원래 List로 재시도를 진행합니다.
3. Skip, Retry 실전 연습
아래와 같이 Skip 정책을 설정하였습니다.
@Bean
@JobScope
public Step skipStep(JobRepository jobRepository) {
return new StepBuilder("skipStep", jobRepository)
.listener(stepExecutionListener)
.<BoardTbEntity, BoardTbEntity>chunk(10, transactionManager)
.reader(skipJpaPagingItemReader())
.processor(skipProcessor())
.writer(skipWriter())
.faultTolerant() // 내결함성 기능 활성화
.skipLimit(3) // skip 허용 횟수, 해당 횟수 초과 시 Error 발생, Skip 사용시 필수 선언
.skip(SQLException.class) // SQLException 에 대해서는 skip
.noSkip(NullPointerException.class) // NullPointerException 에 대해서는 skip 하지 않음
.build();
}
강제 Skip 발생을 위하여 Exception을 발생시켰습니다.
@Bean
@StepScope
public ItemProcessor<BoardTbEntity, BoardTbEntity> skipProcessor() {
return board -> {
BoardTbEntity newBoard = new BoardTbEntity();
newBoard.setCommentCount(board.getCommentCount());
newBoard.setLineType(board.getLineType());
newBoard.setPostType(board.getPostType());
newBoard.setWriterType(board.getWriterType());
newBoard.setStatus(board.getStatus());
newBoard.setView(board.getView());
newBoard.setContentEmbedded(board.getContentEmbedded());
newBoard.setReport(board.getReport());
newBoard.setRecommendCount(board.getRecommendCount());
if (board.getContentEmbedded().getIp() != null && board.getContentEmbedded().getIp().startsWith("1.111.111.111")) {
log.info("강제 SQLException 발생 후 SKIP");
throw new SQLException();
}
return newBoard;
};
}
아래 로그를 보게되면 SKIP이 발생된 이후 정상적으로 처리되는것을 확인할 수 있었다. 또한 배치 스텝 execution 테이블에서 데이터가 1건이 SKIP되고 처리된것을 확인 할 수 있었습니다.
아래와 같이 Retry 정책을 설정하였습니다.
@Bean
@JobScope
public Step retryStep(JobRepository jobRepository) {
return new StepBuilder("retryStep", jobRepository)
.listener(stepExecutionListener)
.<BoardTbEntity, BoardTbEntity>chunk(10, transactionManager)
.reader(retryJpaPagingItemReader())
.processor(retryProcessor())
.writer(retryWriter())
.faultTolerant()
.retry(SQLException.class)
.retryLimit(3)
.build();
}
강제 Retrty 발생을 위하여 Exception을 발생시켰습니다.
@Bean
@StepScope
public ItemProcessor<BoardTbEntity, BoardTbEntity> retryProcessor() {
return board -> {
BoardTbEntity newBoard = new BoardTbEntity();
newBoard.setCommentCount(board.getCommentCount());
newBoard.setLineType(board.getLineType());
newBoard.setPostType(board.getPostType());
newBoard.setWriterType(board.getWriterType());
newBoard.setStatus(board.getStatus());
newBoard.setView(board.getView());
newBoard.setContentEmbedded(board.getContentEmbedded());
newBoard.setReport(board.getReport());
newBoard.setRecommendCount(board.getRecommendCount());
if (board.getContentEmbedded().getIp().startsWith("1.111.111.111")) {
log.info("강제 SQLException 발생 후 RETRY");
throw new SQLException();
}
return newBoard;
};
}
아래 로그를 보게되면 Retry가 발생되고 제한을 초과하여 Exception이 발생되었습니다.. 또한 배치 스텝 execution 테이블에서 실패건으로 적재된 것을 확인 할 수 있었습니다.
https://dev-jwblog.tistory.com/141#article-3--2--step-%EC%97%90%EC%84%9C-skip-%EC%82%AC%EC%9A%A9
'Spring' 카테고리의 다른 글
Spring Boot Batch 적용하기 - 3 (0) | 2024.10.20 |
---|---|
Spring Scope와 ObjectProvider, proxyMode (0) | 2024.10.20 |
Spring Boot Batch 적용하기 - 1 (1) | 2024.09.18 |
Spring Boot + Quartz 적용하기 - 3 (1) | 2024.09.17 |
Spring Boot + Quartz 적용하기 - 2 (1) | 2024.09.16 |