[DDD-START] Ch08. 애그리거트 트랜잭션 관리
in Dev-Study on Architecture
공부하는 내용을 정리하는 목적으로 작성하고 있습니다. 잘못 작성된 내용을 지적해주시면 좀더깊이 공부해서 내용을 수정하겠습니다.
애그리거트의 트랜잭션
서로다른 스레드가 하나의 애그리거트에 동시에 접근하여 상태를 수정하게 된다면 무결성이 깨지게 된다.
예를들어 발송이 시작되기 전에 주문자가 배송정보를 변경하려고 할 때, 운영자가 발송을 해버린다면 운송업체는 기존의 배송정보를 통해 배송을 하게될 것이지만 고객(주문자)는 새로 변경한 배송지로 배송이 올 것을 기대하게 된다.
이런 현상을 막기 위해서는 하나의 애그리거트에 접근한 스레드가 트랜잭션을 완료할 때까지 다른 스레드가 해당 애그리거트로 접근하지 못하게 트랜잭션을 처리해야 한다.
이 때 사용되는 방법은 크게 선점 잠금과 비선점 잠금이 있다.
선점잠금
C++,Java등에서 사용하는 Mutex와 동일한 개념같다.
가장 먼저 접근한 스레드에게 우선권이 주어지고, 해당 스레드가 작업을 완료할 때까지(트랜잭션 처리가 완료되어 커밋되거나 롤백되거나) 잠근 뒤, 다른 스레드는 접근하지 못하고 대기해야 한다.
(ex. 배가 아파 화장실에 갔는데 화장실에 누군가 들어가있다면 나올때까지 기다려야 한다.)
트랜잭션이 완료되고 나면 잠금이 해제되고 가장 앞에서 대기하고 있던 스레드가 다음 우선권을 갖고 트랜잭션을 처리한다.
선점잠금시 문제점 (교착상태)
- Thread1이 애그리거트A 접근하여 잠금
- Thread2가 애그리거트B 접근하여 잠금
- Thread1이 작업완료하여 애그리거트B에 접근 시도 (Block)
- Thread2가 작업완료하여 애그리거트A에 접근 시도 (Block)
만약 3,4가 거의 동시에 일어났고, 각 Thread가 접근하려고 할 때 상대방 Thread가 아직 작업완료되지 않은 상태였다면
두 Thread는 Block상태가 되어 더이상 작업이 진행되지 않는다. (영원히 Blocking)
이런 문제를 방지하려면 Block 상태일 때 최대 대기 시간이 설정되어야 한다.
(예를들어 서버에 접속하려고 하는데 서버가 응답이 없는다면 무한히 기다려야할까? Connection Timeout을 두어 일정 시간이 지나면 접속 에러로 상태가 변경되어야 한다.)
비선점잠금
위의 배송지변경 예제에서, 잠금을 사용한다고 하더라도 아래와 같은 경우 문제가 발생할 수 있다.
운영자와 고객이 동시에 접근하여 각 배송정보를 조회한 상태에서, 고객이 먼저 배송정보를 변경한다고 가정한다.
(운영자는 시스템에서 주문정보를 조회하고, 고객은 시스템에서 배송지변경 기능을 통해 배송지변경 폼을 내려받는다. 두 기능은 서로 다른 트랜잭션)
운영자는 기존 배송정보를 조회했으므로 기존 정보로 배송을 시작하게되고 고객은 잠금을 획득해도 원하는 배송지로 배송받지 못하게 된다. (배송 전에 고객이 아무리 변경하려고 해도 소용없음)
이런 경우, 각 트랜잭션마다 버전정보(ex. SequenceNumber)를 갱신하여 조회했을 때의 버전정보와 갱신하려고 할 때의 버전정보를 비교한다.
두 값이 일치하면 동기화에 문제가 없음이 보장되고 만약 일치하지 않는다면 중간에 누군가에 의해 애그리거트의 상태가 변경된 것이므로 트랜잭션 실패 처리하여 무결성을 유지한다.
비선점 잠금을 사용하려면 클라이언트에서 버전 정보를 알고 있어야하므로 서버는 폼을 내려줄 때 버전정보를 함께 내려보낸다.
비선점 잠금을 사용해도 문제가 되는 경우
선점, 비선점잠금의 기준은 애그리거트의 상태가 변경되었는지 여부로 된다.
규모가 큰 애그리거트의 경우 루트 애그리거트와 하위 엔티티들로 구성되는데 이 때 하위 엔티티의 상태만 변경되었을 경우 루트 애그리거트의 버전정보가 갱신되지 않는 경우가 발생할 수 있다.
따라서 버전정보는 하위 엔티티들까지 변경여부를 확인하여 버전정보를 갱신할 수 있어야 한다.
오프라인 선점 잠금
서로 다른 두 스레드가 동시에 한 트랜잭션에 접근한 경우, 선점 잠금 방식에 의해 하나는 대기하게 된다.
이 때 이미 선점한 스레드가 잠금을 해제하지않고 세션을 종료해버린다면 해당 잠금은 영원히 풀리지 않는 문제가 발생할 수 있다.
따라서 스레드 잠금의 최대 유효시간 설정이 필요하다. (일정시간이 넘어가면 잠금해제되도록)
이 때 또 한가지 문제가 발생할 수 있다.
만약 세션의 잠금유효시간이 1분이라고 가정할 때, 1분1초에 트랜잭션을 커밋하려고 한다면 유효시간이 지나 실패하게 될 것이다.
실컷 작성/수정 폼에서 데이터를 입력하고 저장하려고 하는데 세션 타임아웃이 발생한다면 사용자 입장에서 엄청 불편할 수 있다.
따라서 세션이 유지된 상태라면 자동으로 세션을 연장해줄 수 있어야 한다. (ex. Ajax를 이용한 세션 연장)
DDD-START (최범균님 저) 도서 참조