분산락 적용 주의점 (그라운드 플립 사례)¶
요약
Redisson 분산락을 쓸 때 실제로 당한 함정 2개: ① 락 메서드에 @Transactional을 붙이면 락 해제 후에 커밋되어 다른 스레드가 커밋 전 데이터를 읽음 → 락 클래스와 트랜잭션 클래스를 분리해 커밋이 락 해제보다 먼저 되게 할 것 ② 락 안에서 비동기 이벤트로 DB 삽입을 빼면 그 삽입은 락 밖에서 실행됨 → 락이 보호하는 데이터의 수정·삽입은 전부 락 내부에서 직접 실행. 원칙 한 줄: "락 안에서 접근되는 데이터의 처리 로직은 전부 락 안에서 끝나야 한다." FillMap의 격자 점령 UPSERT(동시 업로드 경쟁) 로직에 분산락을 도입하게 되면 그대로 적용해야 할 체크리스트.
이 노트로 답할 수 있는 질문¶
- 분산락 메서드에 @Transactional을 붙이면 왜 동시성 문제가 재발하나?
- 락 안에서 비동기 이벤트를 발행하면 무엇이 문제인가?
- 락과 트랜잭션의 올바른 순서(커밋 → 락 해제)는 어떻게 보장하나?
- FillMap 격자 점령 로직에 분산락을 넣는다면 뭘 조심해야 하나?
요약¶
- 함정 1:
@Transactional+ 락 → 트랜잭션 커밋이finally { unlock() }뒤에 일어남. 해결: 락 전용 클래스(트랜잭션 없음)가 락 안에서 별도 클래스의 @Transactional 메서드를 프록시 경유로 호출. - 함정 2: 락 안에서
eventPublisher.publishEvent()로 삽입을 위임 → 리스너는 락 없이 실행. 해결: 삽입을 락 내부 동기 코드로. - 보너스(부하 테스트 글에서): 처리 지연으로 leaseTime 초과 시 락이 자동 해제된 뒤 unlock을 또 호출하면 에러 — 해제 전 락 유효성 확인 필요.