Connection Deadlock 이슈

이동욱·2024년 4월 22일
0

TroubleShooting

목록 보기
7/9

Connection Deadlock 이슈

1. 개요


운영 시스템 내에서 계속해서 아래와 같은 Connection Deadlock 로그가 발생했다.

Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.

2. 분석


  • 해당 에러가 발생하는 부분을 보니 로직이 ScheduledExecutorService를 사용하여 일정 주기마다 로직을 실행하는 부분이어서 thread와 connection pool 쪽에 대한 문제가 있다고 생각하고 유심히 로직을 봤다.

  • 그러나 ScheduledExecutorService의 initDelay이 다 다르고 delay은 같았다. (하나만 빼고) 그래서 적어도 겹치는(동시 수행되는) thread는 두 개일텐데 왜 deadlock이 발생하는건지 로직을 좀 더 깊게 확인했다. 일단 해당 에러가 발생했을 시점에 connection pool의 connection이 부족했다는 것이니 ...

3. 원인


  • 에러가 발생했던 지점의 로직을 보니 로직의 최상위 method에 @Transactional이 걸려있지 않아 그 안의 각각의 method에 @Transactional을 타기 때문에 각각의 독립적인 transaction으로 인식해 많은 connection을 사용하고 있다는 것을 알았다.

4. 해결


  • 먼저 해당 로직에서 함수를 사용하는 것들 중 transaction을 얼마나 사용하는지, @Transactional의 속성은 무엇을 사용하고 있는지 전부 분석했다. 먼저 최상위 method에서 현재 상담이 가능한지 체크하는 함수와 상담사 배정을 해주는 함수 두 개를 사용했다. 각각의 함수 또한 @Transactional 처리가 안되어있었고 그 내부에 @Transactional으로 묶인 여러 함수를 사용하면서 더 많은 connection을 사용하고 있었다.

  • 그리고 각각의 함수에 적용되어 있던 @Transactional의 전파 속성은 모두 Propagation.REQUIRED이었다.

    • Propagation : @Transactional의 속성으로 여러 트랜젝션이 있는 경우에 전파 속성
      • REQUIRED
        • 부모 트랜젝션이 존재한다면 합류, 아니면 새로운 트랜젝션 생성
      • REQUIRED_NEW
        • 무조건 새로운 트랜잭션 생성
      • MANDATORY
        • 무조건 부모 트랜젝션에 합류
      • SUPPORTS
        • 메소드가 트랜잭션을 필요로 하지 않지만, 진행 중인게 존재하면 트랜젝션 사용
      • NESTED
        • 부모 트랜젝션이 존재하면 부모 트랜젝션에 중첩, 존재하지 않다면 새로운 트랜잭션 생성
        • 부모 트랜잭션 예외 발생 시 자식 트랜잭션도 rollback
      • NEVER
        • 메소드가 트랜잭션을 필요로 하지 않는다.
  • 따라서 transaction을 포괄적으로 덮기 위해 최상위 method에 @Transactional을 선언해 하나의 transaction에서 처리하게끔 처리했다.

  • 추가적으로 hikariCP의 connection pool size 설정도 확인해봤는데 default인 10으로 되어 있어서 증가시켜주었다.

5. 고찰


이전에도 transactional의 propagation 관련된 에러를 접했던 경험이 있는데 이번 이슈를 해결할 때 좀 헤맸던걸 보면 제대로 이해하지 못했나보다. 특히 @Transactional의 부모, 자식 간의 상관관계에 대해서 제대로 이해할 수 있었다.

관련되어서 찾아봤을 때 https://mangkyu.tistory.com/269 의 글이 많이 도움이 되었다. 트랜잭션이란 시작에서 commit or rollback이 호출될 때 까지가 하나의 트랜잭션으로 묶인다는 글을 보니 뭔가 훨씬 더 쉽게 이해가 갔다.

물리 트랜잭션과 논리 트랜잭션에 대한 개념과 논리 트랜잭션이 여러 개 있는 경우 propagation의 속성에 따라 물리 트랜잭션이 나뉜다는 것을 그림으로 보니 제대로 이해가 갔다.

profile
lduk 웹 개발자(back)

0개의 댓글