CHAPTER 8. 애그리거트 트랜잭션 관리
1. 애그리거트 트랜잭션
- 2개 이상의 트랜잭션을 동시에 실행시켜 데이터를 수정할 경우, 트랜잭션 실행시점에서 애그리거트의 일관성이 깨질 경우가 발생한다.
DBMB가 지원하는 트랜잭션과함께 애그리거트를 위한 추가적인 트랜잭션 처리 방식이 필요하다
- 선점(Pessimisic) 잠금
- 비선점(Optimistic) 잠금
2. 선점 잠금
- 선점잠금은 먼저 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때까지 다른 스레드가 해당 애그리거트를 수정하는 것을 막는 방식이다.
- 한 스레드가 애그리거트를 구하고, 수정하는 동안 다른 스레드가 수정할 수 없으므로 동시에 애그리거트를 수정할 때 발생하는 데이터 충돌 문제를 해소할 수 있다.
Order order = entityManager.find(Order.class, orderNo, LockModeType.PESSIMISTIC_WRITE);
- JPA의 EntityManager는 LockModeType을 인자로 받는 find() 메서드를 제공하는데, LockModeType.PESSIMISTIC_WRITE를 값으로 전달하면 해당 엔티티와 매핑된 테이블을 이용해서 선점 잠금 방식을 적용할 수 있다.
선점 잠금 기능을 사용할 때는 잠금 순서에 따른 교착 상태가 발생하지 않도록 주의해야 한다.
1. 스레드 1: A 에그리거트에 대한 선점 잠금 구함
2. 스레드 2: B 에그리거트에 대한 선점 잠금 구함
3. 스레드 1: B 에그리거트에 대한 선점 잠금 시도
4. 스레드 1: A 에그리거트에 대한 선점 잠금 시도
이런 문제가 발생하지 않도록 하려면 잠금을 구할 때 최대 대기 시간을 지정해야 한다.
- JPA에서 선점 잠금을 시도할 때 최대 대기 시간을 지정하려면 다음과 같이 힌트를 사용하여 조절할 수 있다.
Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.lock.timeout", 2000);
Order order = entityManager.find(Order.class, orderNo, LockModeType.PESSIMISTIC_WRITE, hints);
- 힌트를 사용할 때 주의할 점은 DBMS에 따라 힌트가 적용되지 않는다는 점이다.
- 힌트를 사용하기 전 DBMS가 관련 기능을 지원하는지 미리 확인해야 한다.
3. 비선점 잠금
비선점 잠금 방식은 잠금을 해서 동시에 접근하는 것을 막는 대신 변경한 데이터를 실제 DBMS에 반영하는 시점에 변경 가능 여부를 확인하는 방식이다.
- 비선점 잠금을 구현하려면 애그리거트에 버전으로 사용할 숫자 타입의 프로퍼티를 추가해야 한다.
- 애그리거트를 수정할 때마다 버전으로 사용할 프로퍼티의 값이 1씩 증가하는데, 이 때 다음과 같은 쿼리를 사용한다.
UPDATE aggtable SET version = version + 1, colx = ?, coly = ?
WHERE aggid = ? and version = 현재 버전
- JPA는 버전을 이용한 비선점 잠금 기능을 지원한다. 아래와 같이 버전으로 사용할 필드에 @Version 어노테이션을 붙이고 매핑되는 테이블에 버전을 저장할 컬럼을 추가하기만 하면 된다.
@Entity
@Table(name = "purchase_order")
@Access(AccessType.FIELD)
public class Order {
@EmbeddedId
private OrderNo number;
@Version
private long version;
- JPA는 엔티티가 변경되어 UPDATE 쿼리를 실행할 때 @Version 에 명시한 필드를 이용해서 비선점 잠금 쿼리를 실행한다.
- @Transactional을 이용해서 트랜잭션 범위를 정한 후 충돌이 발생하면 OptimisticLockingFailureException이 발생한다.
- 에러가 발생한 경우 사용자에게 수정할 수 없다는 에러를 응답으로 전송하여 처리하거나, Version을 통해 비교하여 사용하는 경우 버전 충돌을 사용자에게 알려주고 사용자가 알맞는 후속 처리를 할 수 있도록 한다.
4. 오프라인 선점 잠금
- 한 트랜잭션 범위에서 적용되는 선점 잠금 방식이나, 나중에 버전 충돌을 확인하는 비선점 잠금방식으로는 동시에 수정되고 있는 사항을 처리할 수 없다. 이 떄 필요한 것이 오프라인 선점 잠금 방식이다.
- 단일 트랜잭션에서 동시 변경을 막는 선점 잠금 방식과 달리 오프라인 선점 잠금은 여러 트랜잭션에 걸쳐 동시 변경을 막는다.
- 오프라인 선점 방식은 잠금의 유효 시간을 가져야 한다.
- 유효 시간이 지나면 자동으로 잠금을 해제해서 다른 사용자가 잠금을 일정 시간 후에 다시 구할 수 있도록 해야 한다.
오프라인 선점 잠금은 크게 4가지 기능을 제공해야 한다.
- 잠금 선점 시도
- 잠금 확인
- 잠금 해제
- 락 유효시간 연장