DBMS가 지원해주는 트랜잭션 외 JPA에서도 트랜잭션 처리기버을 소개한다.
Pessimistic(선점) Lock(잠금)
: 비관적 잠금Optimistic(비선점) Lock(잠금)
: 낙관적 잠금
DDD 참고
JPA 프로바이더와 DBMS에 따라 잠금 모드의 구현이 다른데, 하이버네이트의 경우LockModeType.PESSIMISTIC_WRITE 잠금 모드로 사용하면 for update
쿼리를 사용해시 선점 잠금을 구현한다.
교착상태
선점 잠금 기능을 사용할 때는 잠금 순서에 따른 교착 상태(Deadlock)가 발생할 수 있다.
1. 스레드1: A 애그리거트에 대한 선점 잠금 구함
2. 스레드2: B 애그리거트에 대한 선점 잠금 구함
3. 스레드1: B 애그리거트에 대한 선점 잠금 시도
4. 스레드2: A 애그리거트에 대한 선점 잠금 시도
- 이 순서에 따르면 스레드1은 영원히 B 애그리거트에 대한 선점 잠금을 구할 수 없다. ⇒ 스레드2가 B 애그리거트에 대한 잠금을 이미 선점하고 있기 때문이다.
- 동일한 이유로 스레드2는 A 애그리거트에 대한 잠금을 구할 수 없다. ⇒ 스레드2가 A 애그리거트에 대한 잠금을 이미 선점하였음.
- 두 스레드는 상대방 스레드가 먼저 선점한 잠금을 구할 수 없어 더 이상 디음 단계를 진행하지 못하며 스레드1과 스레드2는 교착 상태에 빠지게 되는 것이다.
최대 대기 시간
을 지정해야 한 다. JPA에서 선점 잠금을 시도할 때 최대 대기 시간을 지정하려면 다음과 같이 힌트를 사용하면 된다Map<String, Object> hints = new HashMap();
hints.put(“javax.persistence.lock.timeout”, 1000);
entityManager.find(Member.class, memberId, LockModeType.PESSIMISTIC_WRITE, hits);
JPA의 javax.persistence.lock.timeout 힌트는 잠금을 구하는 대기 시간을 밀리초 단위로 지정한다. 지정한 시간 이내에 잠금을 구하지 못하면 익셉션을 발생시킨다.
힌트를 사용할 때 주의할 점은 DBMS에 따라 힌트가 적용되지 않는다는 것이다. 힌트를 이용할 때에는 사용 중인 DBMS가 관련 기능을 지원하는지 확인해야 한다. (DBMS에 대해 JPA가 어떤 식으로 대기 시간을 처리하는지 반드시 확인)
DBMS에 따라 교착 상태에 빠진 커넥션을
쿼리별로 대기 시간을 지정할 수 있고
커넥션 단위로만 대기 시간을 지정할 수 있는 DBMS도 있음.
선점 방식에도 단점이 있다. 바로 같은 애거리거트를 수정하는 것이 아니라 특정 애거리거트를 사용하는 부분과 수정하는 부분이 겹치면 문제가 발생한다. 예를 들어보면 사용자의 집 주소를 수정하고 있을 때 배송서비스가 사용자의 집 주소 정보를 가져가서 배송을 한다고 생각해보자. 이 경우 배송이 시작해버리고 사용자 주소는 변경해봐야 소용이 없어진다.
이를 위해서 사용할 수 있는것이 버전 정보
이다. 만약 사용자가 정보를 수정하고 있을 때 버전이 9였다. 그리고 사용자 정보를 배송서비스가 접근하면서 버전정보가 10이 되었다. 그 다음 사용자 정보를 수정을 저장하려고 할 때 현재 버전정보가 10이기 때문에 오류가 발생하게 만드는 방식이다.
이는 JPA에서 entity에 @Version
속성만 하나 지정해주면 된다. 그럼 @Transactional 애노테이션을 지정하고 작업을 진행하면 되고 만약 애그리거트의 데이터를 조작하려고 할 때 OptimisticLockingFailureException
이 발생하면 이는 버전이 바뀐것이라고 판단하면 된다.
DDD Start 도메인 주도 설계 구현과 핵심 개념 익히기, 최범균 저