도메인 주도 개발 시작하기 애그리거트 트랜잭션 ch8

김종완·2023년 11월 6일

애그리거트 트랜잭션 관리

만약 옷을 판매하는 쇼핑몰에서 고객이 옷을 구매했다고 생각해보자, 고객은 옷을 구매하였고 배송지를 변경하고자 하고 쇼핑몰 운영자는 배송을 준비하기 위해서 배송 준비 중 상태로 변경하고자 한다. 이렇게 될 경우 운영자는 기존 배송지 정보를 이용해서 배송 상태로 변경했는데 그 사이 고객은 배송지 정보를 변경하여 애그리거트의 일관성이 깨질 수 있다. 이러한 문제를 해결하기 위해서 선점잠금과 비선점 잠금 2가지 방식으로 문제를 해결할 수 있다.

선점 잠금(Pessimistic Lock)

  • 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때까지 다른 스레드가 해당 애그리거트를 수정하지 못하게 막는 방식이다.
  • 선점 잠금은 보통 DBMS가 제공하는 행단위 잠금을 사용해서 구현한다. 오라클을 비롯한 다수의 DBMS가 for update와 같은 쿼리를 사용해서 특정 레코드에 한 커넥션만 접근할 수 있는 잠금장치를 제공한다.
  • 스프링 데이터 JPA는 @Lock 애너테이션을 사용해서 잠금 모드를 구현한다.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select m from Member m where m.id = :id")
Optional<Member> findByIdForUpdate(@Param("id") MemberId memberId);
  • 선점 잠금에서 주의해야할 것은 교착 상태이다. 교착 상태는 사용자 수가 많을 때 발생할 가능성이 높아진다. 이러한 문제를 해결하기 위해 최대 대기 시간을 지정할 수 있다.
Map<String, Object> hints = nwe HashMap<>();
hints.put("javax.persistence.lock.timeout", 2000);
Order order = entityManager.find(
	Order.class, orderNo, LockModeType.PESSIMISTIC_WRITE, hints);

비선점 방식

  • 선점 방식으로 모든 문제를 해결 할 수 없기 때문에 비선점 방식도 고려해볼 수 있다. 이 때 비선점 방식을 고려해볼 수 있다. 비선점 잠금 방식은 동시에 접근하는 것을 막는 대신 변경한 데이터를 실제 DBMS에 반영하는 시점에 변경 가능 여부를 확인하는 방식이다.

  • JPA는 버전을 이용한 비선점 잠금 기능을 지원한다. @Version 애너테이션을 붙이고 매핑되는 테이블에 버전을 저장할 컬럼을 추가하면 된다.

@Entity
@Table(name = "purchase_order")
@Access(AccessType.FIELD)
public class Order {
	@EmbeddedId
    private OrderNo number;
    
    @Version
    Private long version
    ...
  • JPA는 엔티티가 변경되어 UPDATE 쿼리를 실행할 때 @Version에 명시한 필드를 이용해서 비선점 잠금 쿼리를 실행한다. 즉, 애그리거트 객체의 버전이 10이면 UPDATE 쿼리를 실행할 때 다음과 같은 쿼리를 사용해서 버전이 일치하는 경우에만 데이터를 수정한다.
update purchase_order SET ...생략, version = versino + 1
WHERE number = ? and version = 10
profile
개발에 재미를 느끼며 꾸준히 성장하는 개발자 김종완 입니다.

0개의 댓글