POEAA : 오프라인 동시성 패턴

낙관적 오프라인 잠금

충돌이 감지되면 트랜젝션을 롤백해 동시 비즈니스 간 충돌을 방지한다.

낙관전 오프라인 잠금

낙관적 오프라인 잠금은 한 세션에서 커밋하려는 변경 내용이 다른 세션의 변경 내용과 충돌하는지 않는지 확인하는 방법으로 이 문제를 해결한다.

작동원리

1. 레코드 버전번호

시스템의 각 레코드에 버전번호를 연결

현재 세션 상의 데이타의 버전 번호와 저장된 레코드의 버전번호를 비교해서 유효하면 커밋을 성공적으로 수행하고 실패하면(즉, 다른 세션에서 이미 업데이트를 해서 일관성이 깨진 상태이면) 롤백을 수행한다.

2. UPDATE문의 where에 모든 필드 포함

비추천

3. 최초 조회결과 저장 후 커밋 직전 동일 쿼리 결과 비교

특정레코드가 읽기가 아닌 동적쿼리 결과에 의존하는 경우 사용

굵은입자잠금

객체 그룹을 하나의 잠금 항목으로 취급함으로써 특정한 일관성 없는 읽기 문제를 해결

예외

콜렉션 항목을 수정하는 경우라면 낙관적 오프라인 잠금으로 충돌을 감지할 수 없음

굵은입자 잠금으로 어느정도 가능하지는 않을까? 그럼 조회한 콜렉션의 버전을 관리해야하는데 녹록할까?

예시

소스코드관리시스템(SCM:source code management)

그러면?

비즈니스 객체를 병합(merge)하는 경우를 생각하면 낙관적 오프라인 잠금에 큰 가치를 더할 수 있다.

항상 마지막에 충돌체크를 하는것이 아니라 중간에 미리미리 커밋을 체크해서 복잡성을 줄일 수 있다면 그것도 의미있다.

사용시점

두 비즈니스 트랜잭션 간에 충돌 가능성이 낮은 경우 유용하다. 충돌 가능성이 높다면 비관적 오프라인 잠금을 사용하는 것이 바람직하다.

비관적 오프라인 잠금

한 시점에 한 트랜젝션만 데이터에 접근할 수 있게 해서 동시 비즈니스 트랜잭션 간 충돌을 방지한다.

비관적 오프라인 잠금

낙관적 오프라인 잠금의 가장 큰 문제는 충돌이 비즈니스 트랜잭션의 마지막에 감지된다는 것이다.

비관적 오프라인 잠금은 이러한 충돌을 미연에 방지한다. 즉, 작업을 시작할 때 대상 데이터에 대한 잠금을 획득함으로써 일단 비즈니스 트랜젝션을 시작하면 동시성 제어 문제 때문에 작업이 실패하는 경우가 거의 없다.

작동원리

잠금유형

  1. 배타적 쓰기 잠금(exclusive write lock) : 데이터를 편집하려는 경우. 두 비즈니스 트랜잭션이 동일한 레코드를 동시에 변경하지 못하게 함
  2. 배타적 읽기 잠금(exclusive read lock) : 항상 최신 데이터를 읽어야 하는 경우. 심각하게 동시성을 제한하므로 특별한 경우에만 사용
  3. 읽기/쓰기 잠금(read/write lock) : 위 두가지 방식의 장점을 결합

올바른 잠금유형 선택 시 고려사항

  • 시스템의 동시성 극대화(성능)
  • 비즈니스 요건 충족
  • 코드의 복잡성 최소화
  • 도메인 모델러와 분석가가 잠금 전략을 이해

잠금 관리자

대부분의 기업 어플리케이션 환경은 웹서버가 복수이므로 DB기반 잠금 관리자가 적합하다. Redis를 기반으로 전용 잠금자를 사용하는 것도 좋을 거 같다

교착상태(deadlock) 해결방안

  1. 잠금을 얻는데 timeout을 건다.
  2. 잠금을 얻을 수 없으면 바로 예외를 발생시킨다.

사용시점

동시 세션 간 충돌 가능성이 높다면 비관적 오프라인 잠금을 사용하는 것이 좋다.

비관적 오프라인 잠금은 낙관적 오프라인 잠금을 보완하는 방법이라는 것과 반드시 필요할 때만 비관적 오프라인 잠금을 사용해야 한다.

굵은 입자 잠금

하나의 잠금으로 여러 관련 객체의 집합을 잠근다.

굵은 입자 잠금(Coarse-Grained Lock)은 여러 객체를 다룰 수 있는 단일 잠금이다. 잠금 동작 자체를 간소화할 수 있으며, 그룹을 잠그기 위해 모든 멤버를 로드할 필요도 없어진다.

작동원리

단일 경합 지점 생성

객체 그룹을 잠그기 위한 단일 경합 지점을 만드는 것. 이를 통해 단 하나의 잠금으로 전체 집합을 잠글 수 있다.

  • 낙관적 오프라인 잠금 : 각 항목이 버전을 공유하면 단일 경합 지점을 만들 수 있다. (버전 공유)
  • 비관적 오프라인 잠금 : 그룹의 각 맴버가 잠금 가능 토큰(lockable token)을 공유해야하며 이를 잠금 (공유된 버전 잠금)
  • 루트 잠금 : 집합체(aggregate)의 루트를 잠금

버전 공유 버전 공유

공유된 버전 잠금 공유된 버전 잠금

루트 잠금 루트 잠금

잠금의 구현은 아주 다양하며 미묘한 차이는 더욱 다양하다. 각자의 요건에 맞는 구현을 찾아야 한다.

사용시점

굵은 입자 잠금을 사용하는 가장 중요한 이유는 비즈니스 요건을 충족하기 위해서이다.

굵은 입자 잠금을 사용할 때의 가장 긍정적인 효과는 잠금을 획득하고 해제하는 부담이 아주 적다는 것이다. 실질적인 잠금의 범위가 좁다.

굵은 입자 잠금을 원할하게 운영하려면 비정상적인 객체 관계를 만들지 않도록 주의해야 한다.

암시적 잠금

프레임워크나 계층 상위 형식 코드에서 오프라인 잠금을 얻을 수 있게 한다.

암시적 잠금

잠금 작업을 개발자가 일일이 할 것이 아니라 애플리케이션(정확히 말하면 프레임워크 등)이 암시적으로 처리하는 것이다.

예시 : 잠금 매퍼

단순하게 프록시로 암시적 잠금을 구현했을 시 흐름도

잠금 매퍼

MJ

MJ
Backend 개발자 사람입니다. 어플리케이션의 복잡성을 다루는 DDD에 관심이 많습니다. 어제보다 더 나은 개발자가 되려고 항상 노력합니다.

spring boot 2.4.x 에서 openfeign + hystrix 통합하기

spring-boot 2.4.x spring-cloud 2020.x 의존성 상황에서 feign.hystrix.enabled=true가 안됨`feign.circuitbreaker.enabled=true` 로 바꿔보지만 openfeign과 hystr...… Continue reading

IDDD 14장. 애플리케이션

Published on June 19, 2018