이번에 프로젝트에서 주문을 생성하는 과정에서 동시성 이슈를 해결한 경험에 대해 공유하려고 합니다.

문제 상황

이번 프로젝트에서 Item은 재고 수량을 가지고 있습니다. 주문이 생성 될때 아이템의 재고 수량 변경 기능에서 동시성 이슈가 발생하였습니다. User1과 User2가 동시에 동일한 데이터베이스 레코드(아이템 재고 수량)을 수정하려고 하기 떄문에 서로 블록될 수 있는 데드락이 발생하였습니다.

1. Thread1 , Thread2 가 동시에 주문 생성이 시작 됩니다.

2. 두 스레드가 orderService.createOrder 메서드에 진입하여 메서드 내에서 각 스레드는 주문에서 요청된 아이템을 기반으로 OrderItem 객체 목록을 생성합니다.

3. OrderItem 객체를 만들 때 각 스레드는 동일한 Item 객체와 상호 작용하는 경우 데드락이 발생하였습니다.

문제 해결

1. Optimistic Rock(낙관적 락)

낙관적 락(Optimistic Lock)

낙관적 락은 충돌이 거의 발생하지 않을 것이라고 가정하고, 충돌이 발생한 경우에 대비하는 방식입니다.

낙관적 락은 JPA에서 버전(Version) 속성을 이용하여 구현할 수 있습니다.

낙관적 락의 특징으로는 충돌 발생확률이 낮고, 지속적인 락으로 인한 성능저하를 막을 수 있습니다.

@Version JPA는 @Version 어노테이션을 통해 엔티티의 버전을 관리할 수 있습니다.

@Version 적용이 가능한 타입은 Long(long), Integer(int), Short(short), Timestamp입니다.

LockModeType의 OPTIMISTIC_FORCE_INCREMENT을 사용하여 동시성 문제를 해결하려고 하였습니다.

OPTIMISTIC_FORCE_INCREMENT은 엔티티가 물리적으로 변경되지 않았지만, 논리적으로 변경되는(Dirty Checking) 경우 버전을 증가하고 싶을때 사용하는 낙관적 락의 옵션입니다.

제가 작성한 로직에서 아이템과 주문아이템의 엔티티가 1:N 관계일때 상품에 재고수량이 변경 되는 경우에는 상품 엔티티의 물리적 변경은 일어나지 않았지만 논리적인 변경이 일어났기 때문입니다.