콘텐츠로 이동

분산락 적용 주의점 (그라운드 플립 사례)

요약

Redisson 분산락을 쓸 때 실제로 당한 함정 2개: ① 락 메서드에 @Transactional을 붙이면 락 해제 후에 커밋되어 다른 스레드가 커밋 전 데이터를 읽음 → 락 클래스와 트랜잭션 클래스를 분리해 커밋이 락 해제보다 먼저 되게 할 것 ② 락 안에서 비동기 이벤트로 DB 삽입을 빼면 그 삽입은 락 밖에서 실행됨 → 락이 보호하는 데이터의 수정·삽입은 전부 락 내부에서 직접 실행. 원칙 한 줄: "락 안에서 접근되는 데이터의 처리 로직은 전부 락 안에서 끝나야 한다." FillMap의 격자 점령 UPSERT(동시 업로드 경쟁) 로직에 분산락을 도입하게 되면 그대로 적용해야 할 체크리스트.

이 노트로 답할 수 있는 질문

  • 분산락 메서드에 @Transactional을 붙이면 왜 동시성 문제가 재발하나?
  • 락 안에서 비동기 이벤트를 발행하면 무엇이 문제인가?
  • 락과 트랜잭션의 올바른 순서(커밋 → 락 해제)는 어떻게 보장하나?
  • FillMap 격자 점령 로직에 분산락을 넣는다면 뭘 조심해야 하나?

요약

  • 함정 1: @Transactional + 락 → 트랜잭션 커밋이 finally { unlock() } 뒤에 일어남. 해결: 락 전용 클래스(트랜잭션 없음)가 락 안에서 별도 클래스의 @Transactional 메서드를 프록시 경유로 호출.
  • 함정 2: 락 안에서 eventPublisher.publishEvent()로 삽입을 위임 → 리스너는 락 없이 실행. 해결: 삽입을 락 내부 동기 코드로.
  • 보너스(부하 테스트 글에서): 처리 지연으로 leaseTime 초과 시 락이 자동 해제된 뒤 unlock을 또 호출하면 에러 — 해제 전 락 유효성 확인 필요.