본문 바로가기

프로젝트/고민

낙관적 락을 적용하여 동시성 문제 해결하기

문제상황

여러명의 이용자가 동시에 주문을 진행하거나 주문을 취소할 경우 즉 멀티 스레드 환경에서 상품의 재고는 안정적으로 변화하는가에 대한 문제를 직면하게 되었다. 

 

결과는 역시나 실패..

 

해결방법

이러한 상황에서 내가 선택한 해결 방법을 소개하고자 한다.

선택지는 다음과 같이 있었다.

 

1. 비관적 락

2. 낙관적 락

3. 네임드 락

4. Redis의 Lettuce 또는 Redisson 라이브러리 활용

 

각 방법에 대한 장단점을 비교하는 글이 아니기에 내가 선택한 낙관적 락에 대해서 설명하도록 하겠다.

 

설명하기에 앞서 낙관적 락을 선택한 이유를 설명하겠다.

우선 mysql dbms를 이용하고 있는 상황이었기 때문에 Redis를 적용시키지 않았다.

또한, 상품중 같은 상품을 여러 사용자가 같은 시간에 주문하거나 주문을 취소할 가능성은 적다고 예상했기 때문에 락이 일어나지 않을 것이라는 가정을 하는 낙관적 락을 이용했다.

 

구현방법

우선 현재의 코드 상황을 설명하겠다.

1. itemService가 이미 구현되어있는 상황

2. 재고 증가, 감소 로직만 충돌 발생시 예외처리를 해주면 되는 상황

3. 트랜잭션 충돌이 자주 발생할 경우 낙관적 락이 아닌 다른 락으로 동시성을 제어하는 상황을 고려

 

위의 상황을 고려했을때 나는 퍼사드 패턴을 이용해 해당 로직을 구현하였다.

 

1. 다음 그림과 같이 @Version 어노테이션을 이용해 엔티티의 버전을 추가한다.

 

이를 통해 얻을 수 있는 효과는 다음과 같다.

1. 트랜잭션이 시작할 경우 해당 엔티티의 버전이 부여되었다가 종료시 버전이 증가하게 된다.

2. 다른 트랜잭션에서 해당 엔티티에 간섭이 일어날 경우 버전을 비교하여 충돌 발생의 여부를 확인하며 예외를 던진다.

2. 다음 그림과 같이 충돌이 발생했을 경우 예외처리를 직접 해준다.

 

예외를 처리하는 방법은 개발자마다 다르게 설정할 수 있다. 나는 다음과 같이 커스텀한 예외를 던져 사용자가 재요청을 하도록 하였다.

 

테스트 코드

코드는 다음과 같다.

멀티 스레드에 안전한 변수를 통해 카운트한 값에 대하여 상품의 재고와 비교하였다.

 

결과는 다음과 같이 성공적이다.

 

마치며

동시성 문제에 대해 처음으로 다루게 되었다. 책을 읽으며 이론으로만 생각했던 내용을 실제로 구현해보니 생각보다 간단하였고 성장한 것 같다. 다음에 다른 방법으로도 구현해 볼 계획이다.