Spring Batch에는 skip-limit라는 설정을 할 수 있습니다.
chunk 단위 처리할 때 skip-limit에 설정된 횟수만큼은 예외가 발생해도 예외를 skip하고 계속해서 잡이 수행됩니다.
이 정도만 알고 있었는데 skip-limit를 실제 적용해보면서 새롭게 알게된 것들이 있어 정리해봅니다.
아래와 같이 chunk 설정을 해서 1개 단위로 처리를 하고 발생하는 예외에 대해서 7번까지 skip하고 진행하고자 했습니다.
<chunk reader="itemReader" processor="itemProcessor" writer="itemWriter"
commit-interval="1" skip-limit="7">
<skippable-exception-classes>
<include class="java.lang.Exception"/>
</skippable-exception-classes>
</chunk>
설정이 잘 동작하는지 테스트를 해보니 processor에서 예외가 발생한 경우 예외가 발생한 item은 skip되고 다음 item 처리로 넘어갔습니다.
하지만 writer에서 예외가 발생한 경우는 skip되지 않고 같은 item이 다시 processor부터 다시 처리되었습니다. 정확히는 한 번 재처리되었습니다.
왜 바로 skip되지 않고 한번 재처리 한 후에 skip되는지 알 수가 없어 검색을 해보니 다음과 같은 내용 때문에 그렇게 동작하는 것이었습니다.
reader, processor는 동작 시에 item 하나씩 처리하지만 writer는 reader, processor에서 처리된 item을 commit-interval 설정 값만큼 list로 받아서 처리합니다.
reader, processor는 처리 중이었던 item을 skip하면 됩니다. writer 처리 로직에서도 예외가 발생하면 예외가 발생한 item만 skip해야하는데
list의 item 중 어떤 것을 처리하다가 발생했는지 Spring Batch로써는 알 수가 없습니다.
그래서 Spring Batch는 예외가 발생했던 list의 item 하나씩 processor, writer를 다시 수행합니다. 이렇게 하기 위해서 commit-interval을 일시적으로 1로 변경합니다.
재수행하면서 에러가 발생했던 item은 에러가 발생할 것이고 에러가 발생하지 않은 item은 성공할 때마다 커밋이 발생합니다. 그리고 재수행 시 에러가 발생할 때 실제로 skip count가 증가하게 됩니다.
(하지만 재수행 시 에러가 재발하지 않을 수도 있기 때문에 skip되는 item이 없을 수 있습니다.)
에러난 item들의 재처리가 끝나고 난 뒤에는 commit-interval 값은 원래 설정대로 되돌아 갑니다.
저는 위 설정에서 commit-interval을 1로 지정했었고 이런 이유로 예외가 발생할 때 마다 처리 중이던 1개의 item이 다시 processor부터 재수행되는 것이었습니다.
위 내용이 표현된 그림이 있습니다.
출처 : https://blog.codecentric.de/files/2012/03/Blog_Transactions_SkipInWrite-1024x512.png
writer에서 에러가 발생했으면 writer만 재수행하면 되는데 굳이 processor부터 재수행하는 이유는,
processor에도 DB 작업이 있을 수 있기 때문입니다. writer에서 에러가 발생하면 트랜잭션이 롤백되기 때문에 processor에서 수행했던 DB 작업들도 롤백됩니다.
그래서 재수행할 경우 다시 processor부터 재수행하는 것입니다. 하지만 processor에서 DB 작업이 없다면 굳이 processor부터 재수행할 필요는 없기 때문에
processor-transactional="true" 설정을 추가하면 writer부터 재수행하게 할 수 있습니다.
(설정을 추가하면 processor 결과를 캐싱해두고 그것을 이용해서 writer부터 다시 시작합니다.)
이런식으로 재수행되면 DB 작업은 롤백되기 때문에 기존에 했던 작업은 취소되지만 DB 작업 이외의 작업들은 같은 작업이 두 번 수행될 수 있다는 점을 유의해야 할 것 같습니다.
참고 자료
'개발 > Spring' 카테고리의 다른 글
Spring KerberosRestTemplate 사용 예제 (0) | 2021.01.08 |
---|---|
Spring Controller에서 파라미터 검증 방법들 (0) | 2020.06.18 |
스프링 @Async를 통한 비동기 처리 및 설정값 (0) | 2020.06.03 |
MyBatis cannot change the ExecutorType when there is an existing transaction 오류 (0) | 2018.04.14 |
Spring Batch commit-interval에 대한 정리 (0) | 2016.12.23 |