[DDD] 도메인 주도 개발 시작하기 Ch8
Chapter 8 애그리거트 트랜잭션 관리
8.1 애그리거트와 트랜잭션
∘ 운영자는 상품의 배송 상태를 바꾸고, 동시에 고객은 배송지 정보를 바꿈. 서로 다른 애그리거트 객체를 이용하지만 트랜잭션 커밋할 때 두 정보가 다 바뀜 → 애그리거트의 일관성이 깨짐
∘ 애그리거트 트랜잭션 처리 방식에는 선점 잠금과 비선점 잠금이 있음
8.2 선점 잠금
∘ 선점 잠금 : 먼저 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때까지 다른 스레드가 해당 애그리거트를 수정하지 못하게 블로킹
∘ 먼저 애그리거트를 사용하던 스레드가 수행하고 트랜잭션을 커밋하면 잠금이 해제됨
∘ 데이터 충돌 문제 해결 가능
∘ 선점 잠금은 보통 DBMS가 제공하는 행단위 잠금을 사용해서 구현
∘ JPA 프로바이더와 DBMS에 따라 잠금 모드 구현이 다름
8.2.1 선점 잠금과 교착 상태
∘ 교착 상태 : 두 스레드가 각각 상대 스레드가 먼저 선점한 잠금을 구할 수 없음
∘ 교착 상태의 발생을 막으려면 잠금을 구할 때의 최대 대기 시간을 지정해야 됨
8.3 비선점 잠금
∘ 운영자가 배송 상태를 변경하는 사이에 고객이 배송지를 변경해버리는 상황은 선점 잠금으로 해결하지 못하는 상황
∘ 비선점 잠금 : 동시에 접근하는 것을 막는 대신 변경한 데이터를 실제 DBMS에 반영하는 시점에 변경 가능 여부를 확인
∘ 새로 커밋하려는 버전이 이미 커밋된 버전과 충돌하면 데이터 수정에 실패
∘ JPA는 버전을 이용한 비선점 잠금 기능을 지원함
∘ 응용 서비스가 버전은 확인할 필요 없이 애그리거트 데이터가 변경되면 트랜잭션이 종료될 때 JPA가 비선점 잠금을 위한 쿼리 실행
∘ 시스템은 애그리거트를 조회할 때 버전 값도 불러옴
∘ 비선점 잠금 방식을 여러 트랜잭션으로 확장하려면 애그리거트 정보와 함께 버전 정보도 화면에 보여줘야 함
∘ 응용 서비스에 보내는 요청 데이터에 사용자가 전송한 버전 값이 포함됨 → 응용 서비스가 그 버전 값과 애그리거트 버전이 일치하는지 확인 후에 일치하면 기능 수행
∘ 표현 계층은 익셉션이 발생하면 사용자에게 버전 충돌이 발생했음을 알림
8.3.1 강제 버전 증가
∘ 애그리거트 내에 있는 구성요소의 상태가 바뀌면 애그리거트의 버전 값이 증가해야 비선점 잠금이 올바르게 동작할 수 있음
∘ JPA는 엔티티를 구할 때 강제로 버전 값을 증가시키는 잠금 모드 지원. EntityManager#find()
8.4 오프라인 선점 잠금
∘ 컨플루언스는 사전에 충돌 여부는 알려주지만 동시에 수정하는 것은 허용함
∘ 누군가가 수정 화면을 보고 있을 때 수정 화면을 실행하지 못하도록 하려면 오프라인 선점 잠금 방식을 이용해야 됨
∘ 선점 잠금 방식과 비선점 잠금 방식은 한 트랜잭션 범위에서만 적용되지만 오프라인 잠금 선점 잠금 방식은 여러 트랜잭션에 적용됨
∘ 영원히 잠금을 구할 수 없는 상황을 막기 위해 잠금 유효 시간 필요
8.4.1 오프라인 선점 잠금을 위한 LockManager 인터페이스와 관련 클래스
∘ 잠금 선점 시도, 잠금 확인, 잠금 해제, 잠금 유효시간 연장 기능이 필요
∘ LockManager 인터페이스
∘ tryLock() : 잠금을 구할 때. 잠금 식별을 위한 LockId 리턴
∘ LockManager#tryLock() : 오프라인 선점 잠금이 필요한 코드에서 잠금 시도할 때
∘ 컨트롤러가 오프라인 선점 잠금 기능으로 데이터 수정 폼에 동시에 접근하는 것을 제어
∘ 잠금 해제할 때 LockId 이용
8.4.2 DB를 이용한 LockManager 구현
∘ checkAlreadyLocked() : 이미 잠금이 선점되었는지 확인
∘ locking() : 잠금 선점
∘ isExpired() : 유효 시간이 지났는지