데드락 발생 원인 정리

  1. 트랜잭션 A가 데이터(Following)를 insert하면서 FK가 걸려있는 레코드(User)에 S-Lock 설정

  2. 트랜잭션 B가 데이터(Following)를 insert하면서 FK가 걸려있는 레코드(User)에 S-Lock 설정

  3. 트랜잭션 A가 User의 follower_count를 변경하면서 X-Lock 설정을 위해 대기

    하지만, B가 해당 레코드에 대한 S-Lock이 설정되어 있으므로 서로 호환되지 않기 때문에 S-Lock이 풀릴때까지 대기

  4. 트랜잭션 B가 User의 follower_count를 변경하면서 X-Lock 설정을 위해 대기

    하지만, A가 해당 레코드에 대한 S-Lock이 설정되어 있으므로 서로 호환되지 않기 때문에 S-Lock이 풀릴때까지 대기

이로 인해 서로 다른 트랜잭션이 같은 자원에 대해 Lock을 가지고 있으며, 서로 Lock을 해제할 때까지 대기하면서 데드락이 발생합니다.


구상한 해결방법

  1. 타겟 유저에 대한 비관적 락으로 직렬화
  2. 이벤트 기반 비동기 집계 (Outbox + Consumer)
  3. 낙관적 락(@Version) + 재시도 정책

낙관락 vs 비관락


첫번째 방법: 타겟 유저에 대한 비관적 락으로 직렬화

팔로잉 코드 흐름 다시보기

  1. 팔로잉 테이블에서 팔로잉 관계 조회 FollowingCommandPort.findByUserIdAndTargetUserId()
  2. 타겟 유저(팔로잉 당하는 유저) 조회 UserCommandPort.findById()