Skip to content

Commit

Permalink
"[ListyWave] 리스트 수정 시, 동시성 이슈 발생 확인 및 해결기" 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
kdkdhoho committed Jul 30, 2024
1 parent 598da7c commit 5fccd9f
Showing 1 changed file with 28 additions and 10 deletions.
38 changes: 28 additions & 10 deletions contents/posts/listywave/sovle-concurrency-problem/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,30 +237,48 @@ public void update(Long listId, Long loginUserId, ListUpdateRequest request) {
![ObjectOptimisticLockingFailureException 발생](object-optimistic-locking-failure-exception.png)

확실히 테이블 접근 순서를 조정하니 데드락은 걸리지 않는 것을 확인할 수 있다.<br>
하지만 현재 문제는 데드락이 아닌, 동시성 문제다! 근본적인 해결책이 아니다.
하지만 현재 문제는 데드락이 아닌, 동시성 문제다! 근본적인 해결책이 아니다.<br>
게다가 위 코드는 기존 코드에 비해 복잡성이 높아졌다. 따라서 적용하진 않겠다.

`ObjectOptimisticLockingFailureException``net.sf.hibernate.StaleObjectStateException.class` 가 추상화된 예외이다.<br>
hibernate가 뱉는 걸로 봐서 JPA 관련 문제인 것 같은데, 그렇다면 `StaleObjectStateException`는 어떨 때 발생할까?<br>
바로 EntityManager에서 DB에 쿼리를 Flush할 때, 값이 변경되는 Row의 Version 또는 타임스탬프가 불일치하거나, delete or update 쿼리를 날렸지만 DB에 존재하지 않을 때 발생한다고 한다.

따라서 DB 레벨에서 데드락은 해결했지만, JPA 레벨에서의 동시성 문제는 여전히 유효하다.<br>
그런데 사실 JPA의 `ObjectOptimisticLockingFailureException`도 DB에서 발생한 예외를 추상화하고 추상화한 결과물이다.<br>
즉, DB 레벨에서의 근본적인 해결책이 필요해보인다.
따라서 DB에서 데드락은 해결했지만, JPA 레벨에서 동시성 문제는 여전히 유효하다.<br>
이제 이를 해결해보자.

## DB Lock
## 어떻게 해결할까?

그렇다면 근본적인 문제인 동시성 이슈를 Lock을 통해 해결해보자.<br>
DB Lock에는 비관적 락, 낙관적 락, 네임드 락으로 동시성을 제어할 수 있다.
해결하기 위해 여러 자료를 찾아본 결과 비관적 락, 낙관적 락, 네임드 락으로 해결할 수 있겠다.

### 어떤 Lock을 사용할까?
### 비관적 락?

비관적 락은 실제 DB Row에 X-Lock을 걸어줌으로써 해결하는 방식이다.<br>
이 방식을 적용하려면 테이블 접근 순서를 List 테이블부터 X-Lock을 걸도록 코드를 수정하면 된다.
JPA를 이용해서 비관적 락을 적용하려면 `@Lock`을 추가해주고 `@Query`로 직접 쿼리를 작성해줘야 한다.<br>
하지만 특정 리스트만 수정하는 것이 아니고 포함되어 있는 많은 데이터도 함께 수정하는 것이므로 오히려 코드 복잡성이 더 증가할 것이다.

### 네임드 락?

네임드 락은 MySQL의 `GET_LOCK()` 함수를 이용해 임의의 문자열에 대해 락을 설정하는 방식이다.<br>
주로 분산환경에서 N대의 WAS가 1대의 DB 서버에 대한 로직을 동기화할 때 유용하게 사용되는 방식이라고 한다.<br>
또한, 복잡한 로직 자체를 하나의 로직으로 묶을 때에도 유용하다. 이 점에서는 네임드 락도 괜찮은 방식인 것 같다.<br>

### 낙관적 락?

낙관적 락은 DB에 락을 걸지 않고 롤백이나 예외가 발생할 경우 애플리케이션 레벨에서 재시도 또는 사용자에게 실패를 응답하는 식으로 처리하는 방식이다.<br>
JPA를 이용해서는 `@Version`을 이용하여 데이터 수정 시점에, 엔티티를 조회한 시점과 수정하는 시점의 버전일 일치하는 지를 비교하고 다를 경우 예외를 뱉는 방식이다.<br>
그러면 개발자는 해당 예외를 잡아서 처리하는 방식이다.

### @Version 없는 낙관적 락 방식 선택!

현재 상황을 다시 되돌아 봤을 때, 문제의 시작은 작성자와 콜라보레이터의 리스트 동시 수정 행위이다.<br>
이 행위가 빈번히 일어날 것인지를 생각해봤을 땐 그렇지 않다고 생각한다.<br>
현재 서비스 홍보를 하지 않은 상태인데다가 콜라보레이터 기능이 활발히 사용되진 않는다.<br>
게다가 리스트 수정 요청 순서는 지켜질 필요는 없다.

또한 현재 WAS와 DB를 1대의 서버로만 운영하고 있고 앞으로 증축할 계획은 없다. (다중 서버 환경이라면 네임드락을 사용하면 좋을 것 같다)<br>
이러한 상황을 두고 봤을 때 **낙관적 **이 적합하다고 생각한다.<br>
또한 현재 WAS와 DB를 1대의 서버로만 운영하고 있고 앞으로 증축할 계획은 없다. 분산 서버 환경이라면 네임드락을 사용하면 좋을 것 같다.<br>
이러한 상황을 토대로 **낙관적 락을 적용함으로써 구현에 소모하는 시간 대비 가장 효율적인 방법으로 해결하는 것**이라고 생각한다.<br>
이제 낙관적 락 방식으로 이를 해결해보자.

JPA에서 낙관적 락을 적용하기 위해선 `@Version` 어노테이션을 추가하면 된다고 한다.<br>
Expand Down

0 comments on commit 5fccd9f

Please sign in to comment.