jiny

[JPA] 좋아요 기능 동시성 테스트 및 락기능 활용하기 본문

서버/JPA

[JPA] 좋아요 기능 동시성 테스트 및 락기능 활용하기

ongjiny 2023. 3. 9. 00:17

좋아요와 같은 기능은 트랜잭션 2개가 동시에 시작되면 조회후 값을 변경시키는 것이라

트랜잭션 격리수준으로 해결이 불가능하다. (아마 2번의 갱실 분실 문제? 라고 불리는 것 같다)

 

동시에 값을 변경하는 경우에는 lock 걸어서 해결해야하며,

JPA를 사용하는 경우 낙관적락을 추가로 사용할 수 있다.

비관적 락은 데이터 베이스에서 제공하는 select for update 구문을 실행시키는 것과 동일하다

 

 

Post 엔티티에서 좋아요 개수를 표시하는 Star 필드를 표시하고 있고

좋아요(star)를 올리기 위해서는 post 조회를 해야한다

그래서 Post를 select 할때 select for update 구문으로 락을 걸 수 있도록 비관적 락을 사용한다

 

방법1. 비관적 락

결과

 

 

방법2. 낙관적 락 

엔티티에 version 애노테이션을 추가한 Integer 필드를 추가한다.

post의 값이 변경되면 아래와 같은 sql이 실행된다 

 

update post set star=?, VERSION=? (버전+1증가)

where id=? and version=? (select 했을 때 버전과 같은지 비교 )

 

select 했을 때 버전과 비교할 때 다르다면 예외를 발생시킨다.

 

@Version 만 사용했을 때 post의 값이 수정되어야만 버전을 체크하는데,

OPTIMISTIC 옵션을 주면 post 의 값이 조회만 되어도 버전을 체크하기 때문에

중간에 다른 트랜잭션의 조회하는 참여조차 막을 수 있다

 

충돌시 발생하는 예외는 스프링 예외 변환기에 의해 ObjectOptimisticLockingFailureException 으로 전환된다

 

해당 예외를 잡아서 직접 처리하면 된다

(ex. 예외가 발생한 트랜잭션은 db에 값이 저장되지 않기 때문에

해당 예외를 잡아 고객에게 일시적인 오류라는 화면을 보여주어 다시 시도하게 하거나,

내부에서 다시 시도하게 하는 등 으로 처리하면 될 것 같다.)

 

참고로 Version 을 추가한 시점부터 새로운 레코드를 생성해야 Integer 값이 추가된다

(기존에 존재하던 레코드는 전부 null)

그래서 테스트가 필요할 경우 새로운 값을 넣거나 update 쿼리로 version 초기 값을 넣어줘야 한다.