강의평가 작성/수정 API : 500 응답 SQL 예외
Closed this issue · 0 comments
@K-Diger 도움 요청드립니다~!
혹시 네이티브 쿼리를 쓰시게 된 맥락을 알 수 있을까요?
김영한님 JPA 책을 보면 비관적 락은 DB의 SELECT ... FOR UPDATE
문으로 수행되고, 도현님 블로그(감사히 보고 있습니다)를 보면 락의 대상은 강의인 것으로 이해했습니다. => 도현님께서 의도하신 쿼리문(SELECT ... lecture FOR UPDATE)을 수행시키도록 코드를 수정해봤습니다.
파일 히스토리를 보니 도현님께서 hotfix를 몇번 하셨더라구요.
혹시 아래에서 제가 생각하는 해결방법에 문제가 있는지 궁금합니다..!
없다면 레포지토리 테스트 추가 후 PR 올리겠습니다
상황
-
12.26 문제 확인
-
강의 평가 작성시 500 에러 - SQL 예외
-
URI
{{host}}:{{port}}/evaluate-posts?lectureId=1
-
request body
{ "lectureName": "english", "selectedSemester": "2023-2", "professor": "john", "satisfaction": 1, "learning": 1, "honey": 1, "team": 1, "difficulty": 1, "homework": 1, "content": "this is sucks. go to hell mr.john!!!!" }
-
response
{ "exception": "InvalidDataAccessApiUsageException", "code": "NO_CATCH_ERROR", "message": "Illegal attempt to set lock mode on a native SQL query; nested exception is java.lang.IllegalStateException: Illegal attempt to set lock mode on a native SQL query", "status": 500, "error": "Internal Server Error" }
-
error log
2023:12:26 19:47:01.653 ERROR --- [http-nio-8080-exec-6] u.s.g.e.GlobalExceptionHandler : code : NO_CATCH_ERROR, message : Illegal attempt to set lock mode on a native SQL query; nested exception is java.lang.IllegalStateException: Illegal attempt to set lock mode on a native SQL query
예상되는 에러 지점
-
LectureRepository
-findByIdPessimisticWrite
메서드의 Lock@Query(value = "SELECT * FROM lecture WHERE id = :id FOR UPDATE", nativeQuery = true) @Lock(value = LockModeType.PESSIMISTIC_WRITE) Lecture findByIdPessimisticWrite(@Param("id") Long id);
디버깅 결과로는 해당 메서드가 호출되는 시점에 위 에러 로그와 동일한 에러가 발생합니다.
Lock을 스프링에 적용해본 적은 없어서 더 공부해보면서 이슈 업데이트하겠습니다
해결
💡 native Query 대신 JPQL (Spring Data Jpa) 사용
JPQL ver.
@Query(value = "SELECT l FROM Lecture AS l WHERE l.id = :id")
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
Lecture findByIdPessimisticWrite(@Param("id") Long id);
Data Jpa ver.
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
Optional<Lecture> findLockedLectureById(Long lectureId);
-> 기능 정상 동작. @Lock
을 통해 비관적 락(FOR UPDATE)이 적용되는걸로 보임.
도현님 블로그의 내용까지 종합했을 때, 현재 이 방법이 제일 좋아보입니다.
- 수행된 쿼리문 (세부 컬럼들은 축소했습니다)
select *
from
evaluate_post evaluatepo0_
where
evaluatepo0_.id=?
select *
from
lecture lecture0_
where
lecture0_.id=? for update
update
evaluate_post
set
# ...
where
id=?
update
lecture
set
# ...
where
id=?
추가로 javax.persistence.lock.timeout
를 사용해서 락의 타임아웃을 설정할 수 있다고 합니다!
비관적 락인 만큼 필요한 설정이라고 생각해요. (솔직히 락 타임아웃이 왜 필요한지 체감은 안 되긴 합니다.ㅋㅋ 수정이 오래 걸리면 오래 걸리는대로 처리해줘야하는거 아닌가 싶기도 하구요)
이 정도까지 갈 일은 없겠지만 mysql 디폴트 타임아웃 시간이 너무 기네요.. 31536초..