회사에서 꽤 크고 중요한 프로젝트를 맡았다.
회사에서 최초로 JPA를 도입했다. (회사에서는 Mybatis만 사용하고 있었다)
ORM이 확실히 객체지향적으로 코드가 깔끔하게 나오는 느낌이었다. 그런데 문제가 있었다.
기존에 Mybatis만 사용하고 있어서 단일로 DataSourceTransactionManager를 사용중이었는데, JPA를 도입하면서 JpaTransactionManager 를 추가 해야 했다.
기존에 DataSourceTransactionManager에 JpaTransactionManager bean을 @Primary 로 하고 테스트 코드로 Mybatis와 JPA 혼합해서 DML을 해보았다.
-
- Mybatis로 select
- JPA로 insert
-
- JPA로 select
- Mybatis로 insert
잘 되었다.
그러나 롤백에서 문제였다.
-
- Mybatis로 select
- (예외 throw)
- JPA로 insert
- 문제
- JPA로 select
- (예외 throw)
- Mybatis로 insert
Mybatis로 insert할 때 롤백이 안되는 문제였다.
프로젝트에 기존에 있던 로직들이 다 Mybatis로 되어 있는데, 현재 상태로는 @Primary로 되어 있어 있는 JpaTransactionManager를 사용하고 있어서 기존 로직들이 롤백이 안됐다.
그래서 JPA에는 JpaTransactionManager를 사용 해야 하고, Mybatis에는 DataSourceTransactionManager를 사용 하여 트랜잭션을 관리 하게끔 해야 했다.
여러 대안이 있었다.
- ChainedTransactionManager
- JtaTransactionManager
- @Primary를 DataSourceTransactionManager 로 변경하고 JPA 로직에만 @Transactional 에 트랜잭션 매니저 명시
ChainedTransactionManager는 후 순위로 커밋하는 트랜잭션에서는 예외가 발생하면 이전 트랜잭션에서 커밋된 부분은 롤백이 안되는 한계가 있었고, 어느 한 쪽의 커넥션 갯수가 부족해서 타임아웃이 발생할 가능성도 우려 됐다. 무엇보다 해당 트랜잭션 매니저는 단순 순차적으로 커밋하는 것일 뿐 2PC가 아니라 도입하기 걱정스러웠다.
JtaTransactionManager는 XA 프로토콜을 사용한 완벽한 2PC로 구현되어 있었다. 그러나 락이 잡히는 시간이 길어질 것 같고, 여러번 디비랑 커넥션을 맺어 퍼포먼스에 이슈가 있을 것 같았다.
그래서 @Primary를 DataSourceTransactionManager 로 변경하고 JPA 로직에만 @Transactional 에 트랜잭션 매니저 명시를 해서 처리했다.
마무리
이번 이슈로 더 성장한 느낌이 들어 좋다.
고민하는 스펙트럼이 넓어졌다.