최범균 님의 "도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지" 책을 정리한 포스팅 입니다.
1. 애그리거트와 트랜잭션
- 서로 다른 스레드/트랜잭션이 같은 애그리거트의 DB를 동시에 수정하면 충돌이 일어날 수 있음
- ‼️ 두 스레드가 동시에 커밋하면 일관성 깨짐
- ➡️ 해당 테이블의 행에 잠금을 걸어 동시 커밋을 막음
예시) 배송 정보 변경
더보기
- 시스템: 배송 상태를 배송으로 변경할 때
- 사용자: 배송지 주소를 변경할 경우
- ➡️ 시스템의 애그리거트 객체와 사용자의 애그리거트 객체는 서로 독립적 (개념적으로는 같지만 물리적으로는 서로 다름)
2. 선점 잠금
- 먼저 들어온 트랜잭션이 커밋될 때까지 같은 행을 접근하는 다른 트랜잭션은 잠시 blocking됨 (대기)
- 먼저 들어온 트랜잭션이 커밋되면 잠금이 해제됨
- ➡️ 다른 트랜잭션이 시작되어 테이블에 접근하게 됨
선점 잠금과 교착 상태
- 선점 잠금은 먼저 락을 획득한 트랜잭션이 해제하기 전까지는 다른 트랜잭션들은 얻을 수 없음 (얻을때까지 blocking됨)
- ‼️ 교착상태에 빠질 수 있음
- ❌ 다음 단계 진행 불가
예시
더보기
- 운영자 스레드: A 애그리거트 락 획득. B 애그리거트 락 요청
- 사용자 스레드: A 애그리거트 락 요청. B 애그리거트 락 획득
- ❌ 운영자 스레드와 사용자 스레드는 영원히 락을 구할 수 없음
설정) 선점 잠금 (JPA)
더보기
@Lock(LockModeType.PESSIMISTIC_WRITE) // 행 잠금
@Query("select o from Order o where o.id = :id")
Optional<Order> findForUpdate(@Param("id") Long id);
해결책: 힌트 사용
- 락을 구하는 시간에 타임아웃 걸기
- ⚠️ 지정한 타임아웃 내로 락을 구하지 못하면 익셉션 발생
예) @QueryHint
더보기
@Lock(MockModeType.PESSIMISTIC_WRITE)
@QueryHint(name = "javax.persistence.lock.timeout", value="2000")
@Query("select m from Member m where m.id = :id")
Optional<Member> findByIdForUpdate(@Param("id") MemberId memberId);
3. 비선점 잠금
- 변경한 데이터를 실제 DBMS에 반영하는 시점에 변경 가능 여부를 확인하는 방식
- 애플리케이션을 통해 db를 갱신할 경우, 시간 차에 의해 발생하는 트랜잭션 충돌에 적용함
예시) 배송 정보 수정
더보기
- 운영자: 배송정보 조회
- 고객: 배송지 변경 요청
- 운영자: 1번에서 조회한 정보를 기준으로 배송 상태 요청
- 운영자의 트랜잭션이 나뉘어져있어 일관성을 고려한 잠금을 사용할 수 있는 상황이 아님
- 운영자는 배송상태를 변경전에 한번 더 확인하지 않으면 일관성 꺠짐
- ➡️ 이러한 문제를 해결하는 것이 비선점 잠금
버전 컬럼
- 애그리거트에 버전으로 사용할 숫자 타입 프로퍼티 컬럼
- 애그리거트를 수정할 때마다 버전으로 사용할 프로퍼티 값이 1씩 증가함
- ✅ 수정할 애그리거트와 매핑되는 테이블의 버전 값이 현재 애그리거트의 버전과 동일한 경우에만 데이터를 수정함
- ❌ 다른 트랜잭션이 먼저 데이터를 수정해서 버전 값이 바뀌면 데이터 수정에 실패함
예시) 버전 갱신 쿼리
더보기
UPDATE aggtable SET version = version+1 where aggid = ? and version = 현재버전
강제 버전 증가
- 애그리거트에 애그리거트 루트 외에 다른 엔티티의 값만 변경될 경우 (기능 실행 도중 루트가 아닌 경우)
- 루트 엔티티값이 바뀌지 않더라도 다른 요소의 값이 바뀌면 논리적으로 그 애그리거트는 바뀐 것
- ✅ 버전값도 증가해야 함
- ⚠️ JPA는 루트 엔티티의 버전 값을 증가시키지 않음
- ➡️ 조회와 동시에 버전을 강제로 증가시켜야 함
설정) JPA - @Lock
더보기
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
Optional<Order> findByIdOptimisticLockMode(OrderNo id);
4. 오프라인 선점 잠금
- 여러 트랜잭션에 걸쳐 락을 획득하고 전체 작업이 끝나야 락을 반환하는 잠금
- 사용자가 여러 단계에 작업을 순차적으로 수행하면서 전체 작업이 완료될 때까지 락을 잡고 있음
- ➡️ 다단계 시나리오에서 사용됨
예) 배송지 수정
더보기
- 배송지 정보 받기
- 배송지 정보 수정 요청
- ✅ 한 사용자가 전체 프로세스를 완료해야 잠금이 풀려야 일관성이 유지됨
- ➡️ 다른 사용자 (운영자)는 ui 접근도 못하게 해야함
오프라인 선점 잠금을 위한 LockManager 인터페이스와 관련 클래스
DB를 이용한 LockManager 구현
'Code' 카테고리의 다른 글
[도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지] 10. 이벤트 (0) | 2025.06.20 |
---|---|
[도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지] 9. 도메인 모델과 바운디드 컨텍스트 (0) | 2025.06.20 |
[도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지] 7. 도메인 서비스 (0) | 2025.06.19 |
[도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지] 4. 애그리거트 (0) | 2025.06.19 |
[도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지] 3. 애그리거트 (0) | 2025.06.18 |