이 포스트는 📔 도메인 주도 개발 시작하기 책을 읽고 공부한 내용을 정리한 포스트입니다.
하나의 애그리거트에 대해서 동시적으로 상태를 수정하려고 하면 어떻게 될까?
각 스레드는 개념적으로 동일한 애그리거트로 보이겠지만 물리적으로는 서로 다른 애그리거트 객체를 사용한다.
이러한 상황에서 각 스레드가 커밋된다면 애그리거트의 일관성이 깨지는 상황이 발생할 수 있다. 그렇다면 동시적인 접근에 대해 어떤 과정을 적용해서 해결할 수 있을까?
트랜잭션 처리 기법을 활용해서 위 방식을 어떻게 적용할 수 있는지 알아보자.
선점 잠금(Pessimistic Lock)은 먼저 요청한 스레드가 사용이 끝날 때까지 다른 스레드가 접근하지 못하게 막는 방식이다.
한 스레드가 애그리거트를 구하고 수정하는 동안 다른 스레드가 수정을 진행할 수 없으므로 동시 수정 문제는 해결할 수 있다.
하지만 선점 잠금은 데드락의 위험성을 내포하고 있다. 예를 들어 사용자 '가'가 A 애그리거트에 대해서 선점 잠금을 요청하고 사용자 '나'가 B 애그리거트에 대해서 선점 잠금을 요청했을 때 사용자 '가'가 B 애그리거트에 대해서 선점 잠금을 요청하고 사용자 '나'가 A 애그리거트에 대해서 선점 잠금을 요청한다면 양쪽 모두 요청이 완료되지 않아 잠금이 해제되지 않는 데드락 상태가 발생한다.
데드락 상태를 해결하기 위해서 선점 잠금에 대한 타임아웃을 지정함으로써 위험성을 해결할 수 있다.
예를 들어 사용자 '가'가 A라는 상태를 조회했고, 사용자 '나'가 선점 잠금을 통해서 상태 A를 B로 변경하였다. 이때 사용자 '가'는 변경된 상태를 조회하지 못했기에 B가 아닌 A 상태를 통해 결과를 도출해 상태 A를 활용해 A-1을 적용하게 된다. 이 문제를 어떻게 해결 할 수 있을까?
앞에서 알아본 선점 잠금 방식으로는 위 문제를 해결할 수 없다. 이 때 우리가 사용하는 잠금이 비선점 잠금이다.
비선점 잠금(Optimistic Lock)은 애그리거트에 버전 정보를 추가하여 애그리거트를 수정할 때마다 버전 정보를 하나씩 증가시키고 수정 전과 비교하여 버전 정보가 맞게 증가했다면 커밋을 아니라면 롤백시키는 잠금 방식이다.
이 방식을 위에서 말했던 문제에 적용한다면 사용자 '나'의 상태 변경은 정상적으로 반영되지만, '가'의 경우 조회한 경우의 버전 정보와 A-1을 적용하고자 했을때의 버전 정보의 차이가 발생하여 롤백이 발생한다.
오프라인 선점 잠금 (Offline Pessimistic Lock)은 여러 트랜잭션에 걸쳐 동시 변경을 막는 잠금 방식으로 첫 번째 트랜잭션을 시작할 때 오프라인 잠금을 선점하고, 마지막 트랜잭션에서 잠금을 해제한다. 잠금이 해제되기 전까지 다른 사용자는 잠금을 요청할 수 없다.
오프라인 선점 잠금도 오류로 인해 트랜잭션이 중간에 강제로 종료되버린 경우 잠금이 해제되지 않아 다른 사용자가 영원히 잠금을 구할 수 없다는 문제점을 갖고 있어 유효 시간을 활용하여 잠금이 해제될 수 있도록 지정한다.