[JPA, MySQL] 같은 유저가 동시 접속을 해서 좋아요를 누르면 동시성 문제가..??
[리뷰 좋아요 트러블 슈팅]
- 1편 : 여러명이 동시에 좋아요를 누르면 데드락과 동시성 문제가..??
- 2편 : 같은 유저가 동시 접속해서 좋아요를 누르면 동시성 문제가..??
여러명이 동시에 좋아요를 누르면 데드락과 동시성 문제가 해결되어 문제를 해결한줄 알았습니다.
하지만 같은 유저가 500대의 전자기기에 동시 접속을 하여 동시에 좋아요를 누르면 이번엔 NonUniqueResultException이 발생하고 있었습니다. 😭
1개의 데이터만 들어가야 하는데, 10개의 데이터가 들어가서 에러가 발생합니다.
MySQL에서도 확인해보니 똑같은 데이터가 10개나 들어있습니다.
1. 테스트 코드 작성하기
이번에는 ExecutorService를 사용해서 테스트를 만들었습니다.
이전에 여러명의 유저로 테스트하는 것은 Thread를 직접 사용했는데, 방법이 두가지 같아서 학습용으로 사용했습니다.
확실히 Executor가 편한 것 같네요.
에러가 발생하는 원인 코드는 다음과 같습니다.
1. 동시에 들어온 요청들이 findByMemberAndReview에서 데이터가 없음을 확인합니다.
2. 여러 개의 요청들이 reviewFavoriteRepository.save()를 통해 데이터를 저장합니다.
3. 다시 리뷰 좋아요 요청이 들어오면 findByMemberAndReview에서 여러 개의 데이터가 응답하여 에러가 발생합니다.
2. ReviewFavorite에 직접적인 락을 걸 수 없으니, 다른 엔티티에 락을..??
지난 글에서 FK를 확인할 때, S Lock을 획득한다는 내용을 파악하였고, 이걸 비관적 락을 사용해 X Lock으로 전환하여 데드락과 동시성 이슈를 해결하였습니다. 그러면 이것도 같은 방법으로 FK에 X Lock을 걸 수 있지 않을까요?
현재 Review 엔티티에는 X Lock이 걸려있으므로 Member 엔티티에만 X Lock을 설정해주면 됩니다.
MemberRepository에 X Lock을 걸어주는 메서드를 만들어줍니다.
테스트를 돌려보니...
성공했습니다!
하지만 X Lock을 걸어주는 것은 성능적으로 매우 좋지 않습니다.
왜냐하면 수많은 요청이 들어온다면 순차적으로 락을 제공하기 때문에 엄청 느려질 것으로 예상됩니다.
3. UNIQUE 무결성 제약 조건 적용
ReviewFavorite는 리뷰에 대한 좋아요 데이터니까 (Member, Review) 쌍의 데이터는 단 1개만 존재해야합니다.
그러므로 데이터베이스에 무결성 제약 조건인 unique 옵션을 적용할 수 있습니다.
위와 같이 @Table을 사용하여 UNIQUE 제약 조건을 추가합니다.
이걸 적용하면 중복된 값을 삽입하려고 할 때 발생하는 예외인 DataIntegrityViolationException을 처리해주면 됩니다.
이제 다시 테스트를 실행해보면..??
MySQL에서 Duplicate entry 에러가 나오지만, 테스트는 성공할 수 있게 됩니다.
저는 X Lock보다 이걸 사용하는게 더 좋아보이네요.