Spring Batch는 mssql 채번을 하기 위한 방법으로 spring에 있는 org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer를 사용한다.

채번 방식은 다음과 같다.

insert ..
select @@identity
delete .. where  id < value


하나의 Job Repository를 쓰고, 동시에 여러 jvm에서 사용하는 경우 문제가 발생된다. 바로 delete 때문이다.
delete 를 범위로 지정하게 되면 여러 jvm에서 접근할 때, deadlock이 발생된다.

즉, A jvm에서 delete .. where id < 3
   B jvm에서 delete .. where id < 4

이렇게 하면서 3번이 db에 저장되어 있다고 하면 , deadlock이 걸리게 된다.

이런 문제를 해결 하기위해서 session을 새로 열어서 테스트해도 deadlock이 걸렸다.

따로 패치해서 테스트를 하였다.
commit
insert ..
rollback
select @@identity

이렇게 하니 deadlock은 걸리지 않았다. 그러나, 범용성에 문제가 있다. session을 재활용하면서 commit를 쓰는 것은 바람직 하지 않았다.

autocommit(false)
insert..
select max(id)_ from .. with (nolock)
delete .. where id = value
commit

transaction을 걸어서 해결해보려고 했지만, delete 하는 곳에서 또 다시 deadlock이 발생했다.

그래서 table에 index에 pk로 인덱스 생성하니 deadlock은 발생하지 않았다. (table scan이 아닌 index seek방식)

그러나....
채번은 넘어갔다 하더라도.. spring batch 내부에서 transaction이 진행되면서 상태 정보를 저장 하는 transaction에서 문제가 발생했다. (select/insert 또는 select/update)

FK로 constraint 연결되어 있기 때문에 값이 조금이나마 이상해지면 data integrity violation exception이 뜨면서 문제가 발생하게 된다.
mssql 뿐 아니라, 모든 db에서 발생할 수 밖에 없는 문제가 생긴다.

단순히 retry로 해결할 수 없는 수준이다...

결국 내가 내린 결론은 이렇다..
방법은 Spring Batch는 job 당 job repository를 하나로 가지고 있으니, 그 철학을 따라야 한다.
여러 JVM에서 동작하는 Spring Batch job들이 하나의 job repository를 가지게 하면 안된다!!


이번 기회에 Spring batch에 대해서 많이 공부하게 된 계기가 된거 같다..




Posted by 김용환 '김용환'

댓글을 달아 주세요