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

김종완·2023년 11월 6일
1

애그리거트 트랜잭션 관리

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