Spring data JPA를 사용하면 기본적으로 HikariCP가 커넥션 풀 구현체로써 사용된다. HikariCP는 위에서 보는 것과 같이 별도의 설정이 없다면 Auto commit 설정이 true로 세팅되게 된다.
이렇게 설정되면, 개발자가 코드 레벨에서 직접 커밋 쿼리를 실행해주지 않아도 알아서 커밋 쿼리를 실행시키게 된다.
이는 개발자를 위한 편의 기능이지만, 실무에서는 단순한 쿼리 하나를 수행하는 경우는 거의 없고, 여러 쿼리를 하나의 트랜잭션에서 처리해야하는 경우가 대부분이다.
그래서 @Transactional 어노테이션을 거의 필수적으로 사용하게 되는데, 이 시점부터 예상치 못한 비효율과 버그가 발생하게 된다. 이번 글에서는 어떤 이슈들이 있는지 살펴보고, 이를 피하기 위한 설정들 또한 알아보도록 하겠다.
@Transactional을 사용하면, 커밋과 트랜잭션 처리를 @Transactional을 기준으로 처리함을 의미한다.
그래서 기본 설정이 Auto commit = true 되어 있어도 @Transacional에 맞게 처리하기 위해서 @Transacional이 붙은 로직을 실행하기 전에 Auto commit을 끄는 조치가 필요해진다.
즉, Auto commit이 true이던 것을 false로 잠시 바꾸고, @Transactional이 붙은 로직을 수행한 후, 다시 Auto commit을 true로 바꾸는 것이다. 이러한 처리로 인해서 로직의 앞과 뒤에 각각 불필요한 Auto commit 관련 쿼리들이 수행되게 됨으로 인해서 불필요한 시간이 낭비되게 되는 것이다.

이 쿼리들은 하나의 쿼리로써는 10ms 이내의 실행 시간이 필요한 가벼운 쿼리이지만, 많은 트래픽을 처리할 때에는 당연히 발목을 잡을 수 있는 실행 시간이 된다고 볼 수 있을 것이다.
@Transactional을 통해서 로직이 실행되면 위와 같은 로직을 타게 된다. super.begin()에서 @Transactional의 readOnly 설정이나 전파 속성 관련 설정을 읽어서 그에 맞게 커넥션을 세팅하게 된다.
그런데, 여기서 doConnectionsFromProviderHaveAutoCommitDisabled이 true가 아니면, Auto commit 여부를 체크하기 위해서 getConnectionForTransactionManagement()이 실행되는데, 이 때 @Trnasactional에 정의한 설정이 읽히지 않고 기본 설정으로 커넥션을 세팅하게 된다.
그래서, 의도한대로 코드가 동작하지 않는 버그가 발생하게 되는 것이다.
spring:
datasource:
hikari:
auto-commit: false
jpa:
properties:
hibernate:
connection:
provider_disables_autocommit: true
위에서 언급한 이슈들은 이렇게 아주 간단한 설정만으로 모두 해소가 가능하다. 이는 Auto commit을 사용하지 않겠다는 의미의 설정이며, 이렇게 설정할 경우 반드시 모든 DB접근 로직에 @Transactional 어노테이션을 잘 붙여주어야한다.
여기서, provider_disables_autocommit가 조금 생소할 수 있는데 위 섹션의 doConnectionsFromProviderHaveAutoCommitDisabled()에 해당하는 설정이며, DBCP(HicariCP)의 Auto commit 설정을 신뢰하여 사용하겠다는 의미를 갖고 있다. 즉, HikariCP 측에 설정한 auto-commit: false가 모든 커넥션에 반영되는 것이다.
간단한 설정만으로 위에서 언급한 2가지 이슈들을 모두 해결할 수 있게 된다.
Auto commit의 기본값이 false이므로 Auto commit설정을 false로 만들었다가 다시 true로 돌려주기 위한 불필요한 set autocommit 쿼리를 제거하여 DB 쿼리 성능을 향상시킬 수 있고, super.begin()이 실행되어 트랜잭션 설정이 정상적으로 이루어진 후에 커넥션을 획득하게 되어, @Transactional에 명시한 설정이 미적용되는 버그 또한 제거할 수 있게 된다.