본 포스트는 트랜잭션 설정에 따른 데드락이 발생하는 원인과 Spring의 Transaction Propagation Level (전파 레벨)에 대한 글입니다.

 

 

프로젝트 오픈 후 특정 인원 몇몇 한정으로 로그인을 시도하면 Timeout이 발생하곤 했습니다. 모니터링 툴에서 보면  해당 트랜잭션이 timeout이 발생할 때까지 active 상태로 있다가 좀비 트랜잭션(?)이 되어 버리는 현상이었습니다.

(인스턴스를 재기동 하기 전까지는 사라지지 않고 계속 문제 트랜잭션으로 표시됩니다.)

 

운좋게 내부 개발자 중 한 명에게 해당 현상이 발생해서 어렵지 않게 원인을 찾을 수 있었습니다. 로그인 시 비밀번호를 틀리거나 다른 사유로 로그인을 실패하게 되면 실패 횟수를 Update 합니다.

UPDATE  TB_USER
   SET  ERR_CNT = ERR_CNT + 1
 WHERE  USER_ID = #{userId}

이렇게 한 번 로그인을 실패한 후 다시 로그인을 해서 성공하게 되면 ERR_CNT를 0으로 Update하는 부분이 있는데 해당 Query에서 트랜잭션이 멈추어 있었습니다. (비밀번호를 틀린 적이 없는 사용자는 해당 Update를 수행하지 않습니다.)

UPDATE  TB_USER
   SET  ERR_CNT = 0
 WHERE  USER_ID = #{userId}

위 Query를 수행하는 Service 및 Dao를 찾아보니 아래 Annotation으로 별도의 트랜잭션으로 설정되어 있었습니다.

@Transactional(propagation = Propagation.REQUIRES_NEW)

Propagation Level

Spring framework에서는 크게 두 가지 방법으로 트랜잭션을 제어합니다. AOP 설정을 사용하는 선언적 트랜잭션 설정과, 위와 같이 별도의 annotation을 사용하여 트랜잭션을 제어하는 방법이 있습니다. 그 중 위와 같이 annotation을 사용하는 방법의 트랜잭션 전파 레벨(Propagation Level)의 종류는 아래와 같습니다.

Propagation.REQUIRED
- default 값이기 때문에 생략할 수 있습니다.
- 부모  트랜잭션 내에서 실행하며, 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성합니다.

Propagation.REQUIRES_NEW
- 매번 새로운 트랜잭션을 시작합니다.(새로운 연결을 생성하여 실행합니다.)

Propagation.NESTED
- 해당 method가 부모 트랜잭션에서 진행될 경우 commit 되거나 rollback 될 수 있습니다.
- 부모 트랜잭션이 없을 경우 Propagation.REQUIRED와 동일하게 작동합니다.

Propagation.MANDATORY
- 부모 트랜잭션 내에서 실행되며, 부모 트랜잭션이 없을 경우 Exception이 발생합니다.

Propagation.SUPPORT
- 부모 트랜잭션이 존재하면 부모 트랜잭션으로 동작하고, 없을 경우 non-transactional 하게 동작합니다.

Propagation.NOT_SUPPORT
- non-transactional 로 실행되며 부모 트랜잭션이 존재하면 일시 정지합니다.

Propagation.NEVER
- non-transactional 로 실행되며 부모 트랜잭션이 존재하면 Exception이 발생합니다.

문제점

정상적으로 로그인을 완료하게 되면, 다른 Session에서 중복 로그인하는 것을 방지하기 위해 아래와 같이 해당 테이블의 SESS_ID를 현재 Session의 jSessionID로 Update 합니다.

UPDATE  TB_USER
   SET  SESS_ID = #{jSessionId}
 WHERE  USER_ID = #{userId}

로그인 프로세스를 진행하고 있는 부모 트랜잭션에서 해당 Record를 Update 중이고 아직 commit 되지 않은 상태에서 REQUIRES_NEW annotation이 붙어 있는 Update가 실행되면서 두 트랜잭션 간 교차 상태가 발생한 내용 이었습니다.

 

해결

오류 횟수를 0으로 Update하는 부분을 별도 처리하게 된 히스토리가 명확하지 않았지만, 충분히 영향도 검토를 진행한 후에 해당 annotation을 삭제하여 문제를 해결하였습니다.

300x250

+ Recent posts