[DDD START] 8장 애그리거트 트랜잭션 관리 - 01. 비관적 잠금

한 애그리거트를 두 사용자가 동시에 변경할 때 트랜잭션이 필요하다.

애그리거트에 대해 사용할 수 있는 대표적인 트랜잭션 처리 방식에는 비관적(Pessimistic, 선점) 잠금과 낙관적(Optimistic, 비선점) 잠금 두가지 방식이 있다.

비관적(Pessimistic, 선점) 잠금

먼저 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때가지 다른 스레드가 해당 애그리거트의 수정을 막는 방식

비관적 잠금의 작동 방식

배송지 정보 수정과 배송 상태 변경을 동시에 하는 문제에 비관적 잠금을 적용

고객 스레드가 배송지 변경을 하는 시점은 배송 상태이므로 배송지 변경 시 ‘이미 배송이 시작되어 배송지 변경을 할 수 없습니다’ 와 같은 에러가 발생하고 트랜잭션이 실패하게 된다.

비관적 잠금은 보통 DBMS가 제공하는 행 단위 잠금을 사용해서 구현한다. 다수 DBMS가 for update와 같은 쿼리를 사용해서 특정 레코드에 한 사용자만 접근할 수 있는 잠금 장치를 제공한다.

JPA에서 비관적 잠금을 적용하는 방법은 아래와 같다.

Order order = entityManager.find(Order.class, orderNo, LockModeType.PESSIMISTIC_WRITE)

교착 상태

다음과 같은 순서로 잠금을 시도한다면 교착 상태에 빠지게 된다.

  1. 스레드1: A 애그리거트에 대한 비관적 잠금 구함
  2. 스레드2: B 애그리거트에 대한 비관적 잠금 구함
  3. 스레드1: B 애그리거트에 대한 비관적 잠금 시도
  4. 스레드2: A 애그리거트에 대한 비관적 잠금 시도

JPA의 경우 잠금 시도 최대 대기 시간을 지정해서 교착 상태를 회피할 수 있다. 지정한 시간 내에 잠금을 구하지 못하면 예외가 발생한다.

Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.lock.timeout", 2000);
Order order = entityManager.find(
    Order.class, orderNo, LockModeType.PESSIMISTIC_WRITE, hints
);

주의사항

DBMS가 관련 기능을 지원하는지 확인해야 한다.

Reference