선점 잠금은 교착 상태에 빠질 위험이 있다.
선점 잠금은 먼저 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때까지 다른 스레드가 해당 애그리거트를 수정하지 못하게 막는 방식이다.
스레드2는 스레드1이 애그리거트에 대한 잠금을 해제할 때까지 블로킹된다.
스레드1이 트랜잭션을 커밋한 뒤에 스레드2가 애그리거트를 구하게 되므로 스레드2는 스레드1이 수정한 애그리거트의 내용을 보게 된다.
선점 잠금은 보통 DBMS가 제공하는 행단위 잠금을 사용한다.
for update와 같은 쿼리를 사용해 특정 레코드에 한 커넥션만 접근할 수 있는 잠금장치를 제공한다.
스프링 데이터 JPA는 @Lock 어노테이션을 사용해 잠금 모드를 지정한다.
하이버네이트는 PESSIMISTIC_WRITE를 잠금 모드로 사용하면 for update 쿼리를 이용한다.
교착 상태를 주의해야 한다.
사용자 수가 많아질수록 교착 상태에 빠지는 스레드가 증가하고, 시스템은 아무것도 할 수 없는 상태가 된다.
이를 방지하기 위해 잠금을 구할 때 최대 대기 시간을 지정해야 한다.
"javax.persistence.lock.timeout" 힌트는 잠금을 구하는 대기 시간을 밀리초 단위로 지정해 익셉션을 발생시킨다.
DBMS에 따라 힌트가 적용되지 않을 수 있기 때문에 관련 기능을 지원하는지 확인해야 한다.
스프링 데이터 JPA는 @QueryHints 어노테이션을 사용해 쿼리 힌트를 지정할 수 있다.
애그리거트의 일관성이 깨지는 현상을 막기 위한 첫번째 방법으로 선점(비관적) 잠금이 있다.
이는 잠금을 구한 스레드가 잠금을 해제하기 전까지 다른 스레드가 접근하지 못하도록 막는 방법이다.
처음 잠금을 구한 스레드가 잠금을 해제하지 않는다면 다른 스레드는 잠금을 구하지 못해 교착 상태에 빠지게 되기 때문에 주의가 필요하다.
쿼리 힌트를 사용해 쿼리별로 대기 시간을 설정하거나 커넥션 단위로 대기 시간을 지정해 최대 대기 시간을 설정해 교착 상태를 방지해야 한다.
DB의 Lock 개념에 대해 조금은 알고 있어서 선점 잠금에 대해 이해가 잘 되었다.