도메인 주도 개발 시작하기 : 8장 애그리거트 트랜잭션 관리

일단 해볼게·2025년 8월 17일
0

book

목록 보기
25/31

8.1 애그리거트와 트랜잭션

  • 한 주문 애그리거트에 대해 운영자와 고객이 동시에 애그리거트를 수정한다면?

    • 트랜잭션 처리 방식
      • 선점 잠금 → 비관적 잠금
      • 비선점 잠금 → 낙관적 잠금

8.2 선점 잠금

  • Pessimistic Lock
  • 먼저 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때까지 다른 스레드가 해당 애그리거트를 수정하지 못하게 막는 방식

  • 잠금 해제 전까지 블로킹된다.
    • 접근, 수정 잠금
  • DBMS가 제공하는 행 단위 잠금 사용
    • select for update
      • JPA의 EntityManager는 LocakModeType.PESSIMISTIC_WRITE 모드 제공
  • 교착 상태(deadlock)가 발생하지 않도록 주의
    • 잠금을 구할 때 최대 대기 시간 지정

      Map<String, Object> hints = new HashMapo<>;
      hints.put("javax.persistence.lock.timeout", 2000);
      Order order = entityManager.find(
      Order.class, orderNo, LockModeType.PESSIMISTIC_WRITE, hints);
    • DBMS에 따라 힌트가 적용되지 않을 수도 있다.

      public interface MemberRepository extends Repository^ember^ Memberld> {
      	@Lock(LockModeType.PESSIMISTIC_WRITE)
      	@QueryHints({
      		@QueryHint(name = "javax.persistence.lock.timeout", value = "2000")
      	})
      @Query("select m from Member m where m.id = :id")
      Optional<Member> findByIdForUpdate(@Param("id") Memberld memberld);
    • DBMS에 따라 쿼리별로 대기시간을 정할 수 있고, 커넥션 단위로 대기시간을 정할 수 있다.

8.3 비선점 잠금

  • Optimistic Lock

  • 문제는 운영자가 배송지 정보를 조회하고 배송 상태로 변경하는 사이에 고객이 배송지를 변경한다는 것이다.
    • 선점 잠금으로 해결할 수 없다.
      • 동시에 같은 컬럼을 갱신하는 경우만 막아준다.
  • 비선점 잠금은 동시에 접근하는 것을 막는 대신 변경한 데이터를 실제 DBMS에 반영하는 시점변경 가능 여부를 확인하는 방식이다.
    • version 컬럼을 추가해 현재 버전과 일치하는지 체크한다.
      - 수정 시 version을 올린다.

      UPDATE aggtable SET version = version + 1, colx = ?, coly = ?
      WHERE aggid = ? and version = 현재버전
    • 예시 >
      - 주문 row에 version 칼럼을 둠.
      - 조회 시 version=1 → 운영자가 update할 때 where id=? and version=1
      - 고객이 먼저 update하면 version=2가 되므로, 운영자의 update는 영향 row=0 → 실패 감지 가능
      - 이 실패를 잡아 "데이터가 변경되었으니 다시 조회 후 처리하세요"로 안내하면 해결

  • OptimisticLockingFailureException
    • 스프링 프레임워크에서 발생
    • 누군가 거의 동시에 애그리거트를 수정
  • VersionConflictException
    • 응용 서비스 코드에서 발생
    • 누군가 애그리거트를 수정
  • 루트 애그리거트 외에 다른 엔티티의 값이 변경된 경우
    • 애그리거트 관점에서는 애그리거트의 일부 요소가 바뀌면 논리적으로 애그리거트가 바뀐 것이다.
    • 따라서 루트 애그리거트의 버전도 증가해야한다.
      • LockModeType.OPTIMISTIC_FORCE_INCREMENT 사용

8.4 오프라인 선점 잠금

  • Offline Pessimistic Lock
  • 여러 사용자가 동시에 한 문서를 수정할 때 발생하는 충돌 사전 방지
    • 컨플루언스는 사전에 충돌 여부 알려주지만 동시 수정은 막지 않는다.
  • 여러 트랜잭션에 걸쳐 동시 변경을 막는다.
  • 첫 번째 트랜잭션에서 오프라인 잠금을 선점하고 마지막 트랜잭션에서 잠금을 해제한다.

  • 과정 3에서 수정 요청을 수행하지 않고 종료한다면?
    • 잠금을 구할 수 없다. 따라서 잠금 유효 시간을 가져야 한다.
  • 오프라인 선점 잠금을 위한 LockManager 인퍼테이스
    public interface LockManager {
    	Lockld tryLock(String type. String id) throws LockException;
    
    	void checkLock(LockId lockld) throws LockException;
    
    	void releaseLock(LockId lockld) throws LockException;
    
     void extendLockExpiration(LockId lockld, long inc) throws LockException;
     }
    • tryLock은 잠금을 식별할 때 사용할 LockId 반환
  • DB를 이용한 LockManger를 구현한다면?
    create table locks (
     'type' varchar(255),
     id varchar(255),
     lockid varchar(255),
     expiration_time datetime, // 잠금 유효 시간 보관
     primary key ('type', id)
     ) character set utf8;
    
     create unique index locks idx ON locks (lockid);
profile
시도하고 More Do하는 백엔드 개발자입니다.

0개의 댓글