카테고리 없음

데이터 중심 애플리케이션 설계 (9장 일관성과 합의)

보리시스템 2024. 7. 8.

[2부 분산 데이터]

09장: 일관성과 합의
1. 일관성 보장

2. 선형성

2.1 시스템에 선형성을 부여하는 것은 무엇인가?
2.2 선형성에 기대기
2.3 선형성 시스템 구현하기
2.4 선형성의 비용

3. 순서화 보장

3.1 순서화와 인과성
3.2 일련번호 순서화
3.3 전체 순서 브로드캐스트

4. 분산 트랜잭션과 합의
4.1 원자적 커밋과 2단계 커밋(2PC)
4.2 현실의 분산 트랜잭션
4.3 내결함성을 지닌 합의
4.4 멤버십과 코디네이션 서비스

[정리]

- 인기 있는 일관성 모델 '선형성'

=> 목적? 복제된 데이터가 오직 하나의 복사본만 있는 것처럼 보이게 하고 데이터에 대한 모든 연산을 원자적으로 만드는 것
=> 장점? 데이터베이스가 단일 스레드 프로그램의 변수처럼 동작하게 만듦
=> 단점? 느림. 네트워크 지연이 큰 환경에서 특히 그러함
=> 일관성과의 차이점? 모든 언산을 하나의 전체 순서가 징해진 타임라인에 넣음

- 시스템에서 발생한 이벤트에 순서를 부과하는 '인과성'
=> 인과성? 원인과 결과를 기반으로 어떤 것이 어떤 것 보다 먼저 실행됐는지
=> 선형성과의 차이점? 더 약한 일관성 모델을 제공
=> 어떤 연산들은 동시에 실행될 수도 있어서 버전 기록은 가지치기(branching)와 합치기(merging)가 있는 타임라인과 같음
=> 인과적 일관성은 선형성의 코디네이션 오버헤드가 없고 네트워크 문제에 훨씬 덜 민감

- 합의
=> 인과적 순서를 담아내더라도 이떤 것들은 이 방법으로 구현할 수 없음
*예: 사용자명을 유일하게 만들고 동일한 사용자명으로 동시에 등록하지 못하게 거부하는 것을 보장
=> 합의를 달싱하는 것은 결정된 것에 모든 노드가 동의하고 결정을 되돌릴 수 없는 방식으로 뭔가를 결정한다는 뜻임
=> 광법위한 문제가 실제로는 합의로 환원될 수 있고 서로 동일함
* 이들 중 하나에 대한 해결책이 있으면 그것을 쉽게 다른 것 중 하나의 해결책으로 변환할 수 있다는 점에서
=> 이러한 동일한 문제의 예
1) 선형성compare-and-sel 레지스터
* 레지스터는 현재 값이 연산의 매개변수로 넘겨진 값과 같은지 여부에 따라 값을 설정할지 말지 원자적으로 결정해야 함
2) 원자적 트랜잭션 커밋
* 데이터베이스는 분산 트랜잭션을 커밋할 것인지 어보트할 것인지 결정해야 함
3) 전체 순서 브로드캐스트
* 메시징 시스템은 메시지를 전달할 순서를 결정해야 함
4) 잠금과 임차권
* 여러 클라이언트들이 잠금이나 임차권을 얻기 위해 경쟁하고 있을 때 잠금은 누가 성공적으로 잠금을 확득할지 결정
5) 멤버십/코디네이션 서비스
* 타임아웃 등 장애 감지기 주어지면 시스템은 어떤 노드는 살아있고 어떤 노드는 세션 타임아웃이 발생해 죽었다고 생각돼야 하는지 결정해야 함
6) 유일성 제약 조건
* 여러 트랜잭션들이 동시에 같은 키로 충돌되는 레코드를 생성하려고 할 때 이 제약 조건은 어떤 것을 허용히고 어떤 것을 재익 조건 위반으로 실패하도록 할 것인지 결정해야 함

- 이 모든 것들은 노드가 하나만 있거나 결정하는 능력을 한 노드에만 준다고 하면 간단함
=> 단일 리더 데이터베이스에서 일어나는 일
=> 결정을 하는 모든 능력은 리더에게만 부여되는데 이것이 단일 리더 데이터베이스가 선형성 연산과 유일성 제약조건, 전체순서가 정해진 복제로그 등을 제공할 수 있는 이유임
=> 그 단일 리더에 장애가 나거나 네트워크가 끊겨서 리더에 접속할 수 없게 되면 이런 시스템은 아무 진행도 하지 못하게 됨
=> 이러한 상황 처리 방법
1) 리더가 복구될 때까지 기다리고 시스템이 그동안 차단되는 것을 받아들임. 여러 XA/JTA 트랜잭션 코디네이터는 이 선택지를 채택함. 이 방법은 종료 속성을 만족하지 않기 때문에 합의를 완전히 해결하지는 않음. 리더가 복구되지 않으면 시스템은 영원히 차단됨
2) 사람이 새 리더 노드를 선택하고 시스템이 그 노드를 사용하도록 재실정해서 수동으로 장애 복구를 함. 많은 관계형 대이터베이스가 이 방법을 취함. "불가항력"에 의한 일종의 합의임. 컴퓨터 시스템 밖에 있는 인간 운영자가 결정을 내림. 장애 복구 속도는 사람이 행동하는 속도로 제한되며 일반적으로 컴퓨터보다 느림
3) 자동으로 새 리더를 선택하는 알고리증을 사용. 이 방법은 합의 알고리즘이 필요하고 불리한 네트워크 조건을 올바르게 처리하는 입증된 알고리즘을 사용하는 것이 좋음

- 단일 리더 데이터베이스는 모든 쓰기마다 합의 알고리즘을 실행하지 않고 선형성을 제공할 수 있지만 리더십을 유지하고 리더십 변경을 위해서는 여전히 합의가 필요
=> 합의를 달성하는 내결함성을 지닌 알고리즘과 시스템이 존재한다는 것

- 모든 시스템이 반드시 합의가 필요한 것은 아님
=> 리더 없는 복제 시스템과 다중 리더 복제시스템은 보통 전역 합의를 사용하지 않음
=> 이런 시스템에서 발생하는 충돌은 다른 리더에 걸친 합의가 없어서 생긴 결과지만 괜찮을 것임
=> 선형성 없이 대처하고, 가지치기와 합치기의 비전 기록이 있는 데이터를 잘 처리하는 법을 배울 필요가 있음

 


 

9장 일관성과 합의

- 분산 시스템에서의 결함을 다루는 가장 간단한 방법은 전체 서비스가 실패하도록 하고 사용자에게 오류 메시지를 보여주는 것

- 내부 구성 요소 중 결함이 있더라도 서비스는 올바르게 동작할 방법을 찾아야 함
=> 내결합성을 지닌 분산 시스템을 구축하는 데 쓰이는 알고리즘과 프로토콜
=> 네트위크에서 패킷이 손실되고, 순서가 바뀌고, 중복되거나 입의 시간 동안 지인이 발생할 수 있음
=> 시간은 최선을 다하더라도 근사치 밖에 쓸 수 없음
=> 노드는 멈출 수 있고 인제라도 죽을 수 있음

- 추상화, 합의 구현
=> 내결합성을 지닌 시스템을 구축하는 가장 좋은 방법은 유용한 보장을 해주는 빔용 추상화를 찾아 이를 구현하고 애플리케이션에서 이 보장에 의존하게 하는 것
=> 분산 시스템에 가장 중요한 추상화 중 하나는 합의. 즉 모든 노드가 어떤 것에 동의하게 만드는 것임
=> 합의 관련 문제 해결 알고리즘
=> 분산 시스템의 보장과 추상화 범위

 

1. 일관성 보장

- 복제 데이터베이스는 대부분 최소한 최종적 일관성을 제공
=> 데이터베이스에 쓰기를 멈추고 불특정 시간 동안 기다리면 결국 모든 읽기 요청이 같은 값을 반환
=> 네트워크 결함도 결국에는 복구된다고 가장할 때 불일치는 일시적이며 결국스스로 해소

- 하지만 이는 매우 약한 보장으로 수렴될 때까지 읽기는 뭔가를 반환할 수도 아무 것도 반환하지 않을 수도 있음
=> 최종적 일관성은 보통의 단일 스레드 프로그램에 있는 변수의 동작과 매우 다르므로 어려움이 있음
* 어떤 변수에 값을 할당한 후 곧 그 변수를 읽으면 예전 값이 읽히거나 읽기가 실패하기를 예상하지 않음

- 약한 보장만 제공하는 데이터베이스를 다룰 때는 그 제한을 계속 알아야 하고 뜻하지 않게 너무 많은 것을 가정하면 안됨
=> 대부분의 시간 동안 애플리케이션이 잘 동작할 수도 있기 때문에 버그는 종종 미묘하며 테스트로 발견하기 어려움
=> 최종적 일관성의 에지 케이스는 시스템에 결함(에를 들어 네트워크 끊김)이 있거나 동시성이 높을 때만 분명히 드러남

- 강한 보장을 제공하는 시스템
=> 성능이 나쁘거나 약한 보장을 제공하는 시스템보다 내결합성이 약할 수도 있음
=> 하지만 강한 보장은 올바르게 사용하기 쉬움

- 9장에서 살펴볼 내용
1) 가장 강한 일관성 모델 중 하나인 선형성(linearizab ity)
2) 분산 시스템에서의 이벤트 순서화 문제(인과성, 전체 순서화 관련 문제)
3) 분산 트랜잭선을 원자적으로 커밋하는 방법(합의 문제의 해결책)


2. 선형성

- 데이터베이스 복제본이 하나만 있다고 한다면 복제 지연을 걱정할 필요가 없음
=> 선형성을 뒷받침하는 아이디어임
=> 원자적일관성(atomic consistency), 강한 일관성(strong consistency), 즉각 일관성(immediate consistency), 외부 일관성(external consistency)

- 선형성은 최신성 보장임(recency guarantee)
=> 읽힌 값이 최근에 갱신된 값이며 뒤쳐진 캐시나 복제본에서 나온 값이 아니라고 보장해준다는 뜻

 


 
2.1 시스템에 선형성을 부여하는 것은 무엇인가?

- 선형성을 뒷반침하는 기본 아이디어
=> 시스템에 데이터 복사본이 하나뿐인 것처럼 보이게 만드는 것

- 선형성 vs 직렬성
=> 선형성은 직렬성과 혼동하기 쉬움
1) 직렬성
=> 모든 트랜잭션이 여러 객체를 읽고 쓸 수 있는 상황에서의 트랜잭션들의 격리 속성임. 트랜잭션들이 어떤 순서에 따라 실행되는 것처럼 동작하도록 보장해줌. 그 순서가 트랜잭션들이 실제로 실행되는 순서와 달라도 상관없음
2) 선형성
=> 레지스터(개별 객체)에 실행되는 읽기와 쓰기에 대한 최신성 보장임. 선형성은 연산을 트랜잭션으로 묶지 않아서 충돌 구체화 같은 부가적인 수단을 사용하지 않으면 쓰기 스큐 같은 문제를 막지 못함

 



2.2 선형성에 기대기

- 선형성이 유용한 예(시스템이 올바르게 동작하도록 만들기 위해 선형성이 중요한 요구사항이 되는 영역)
1) 잠금과 리더 선출

=> 단일 리더 복제를 사용하는 시스템은 리더가 여러 개(스플릿 브레인)가 아니라 진짜로 하나만 존재하도록 보장해야 함
=> 리더를 선출하는 한가지 방법은 잠금을 사용하는 것
=> 선형성 잠금은 트랜잭션 실행의 중요 경로에 있으므로 RAC를 배치할 때는 보통 데이터베이스 노드들 사이의 통신용으로 전용 클러스터 연결 네트워크를 사용

2) 제약 조건 과유일성 보장
=> 데이터가 기록될 때(두 사람이 동시에 이름이 같은 사용자나 파일을 생성하려고 한다면 이들 중 한명은 오류를 반환받는 식으로) 제약 조건을 강세하고 싶다면 선형성이 필요
=> 실제 애플리케이션에서는 때로 이런 제약조건을 느슨하게 다뤄도 됨(예를 들어 항공편 좌석이 초과 예약됐다면 고객을 다른 항공편으로 옮기고 불편함에 대한 보상을 해줄 수 있음). 이런 경우에는 선형성이 필요없을 수도 있음
=> 그러나 관계형 데이터베이스에서 전형적으로 볼 수 있는 엄격한 유일성 제약조건은 선형성이 필요. 외래키나 속성 제약조건 같은 다른 종류의 제약조건은 선형성을 요구하지 않고도 구현할 수 있음


3) 채널 간 타이밍 의존성
=> 웹 서버와 이미지 크기 변경 모듈은 파일 저장소와 메시지큐를 모두 써서 통신하므로 경쟁조건이 발생할 가능성이 열려있음
=> 선형성의 최신성 보장이 없으면 이 두 채널 사이에 경쟁조건이 발생할 수 있
=> 선형성이 경쟁조건을 회피하는 유일한 방법은 아니지만 이해하기에 가장 단순함. 부가적인 통신 채널을 제어한다면 복잡성이 추가되는 대신 비슷한 대안적인 방법을 사용할 수도 있음

 



2.3 선형성 시스템 구현하기

- 선형성 시맨틱을 제공하는 시스템을 구현하는 방법
=> 복제를 사용하는 것임

- 선형성과 정족수
=> 다이나모 스타일 복제를 하는 리더 없는 시스템은 선형성을 제공하지 않는다고 보는 게 맞음

 



2.4 선형성의 비용

- 두 데이터센터 사이에 네트워크가 끊긴다면? (네트워크가 끊기면 선형성과 가용성 사이에서 선택해야 함)
1) 다중 리더 데이터베이스
를 사용하면 각 데이터센터는 계속 정상 동작할 수 있음. 한 데이터센터에 쓰여진 내용이 비동기로 다른 데이터센터로 복제되므로 쓰기는 그냥 큐에 쌓였다가 네트워크 연결이 복구되면 전달됨
2) 단일 리더복제
를 사용하면 리더가 데이터센터 중 하나에 있어야만 함. 모든 쓰기와 선형성 읽기는 리더로 보내져야 함. 따라서 팔로워 데이터센터로 접속한 클라이언트에서 보낸 읽기와 쓰기 요청은 네트워크를 통해 동기식으로 리더 데이터센터로 전송돼야 함
3) 단일 리더 설정
에서 데이터센터 사이의 네트워크가 끊기면 팔로워 데이터센터로 접속한 클라이언트들은 리더로 연결할 수 없으므로 데이터베이스에 아무것도 쓸 수 없고 선형성 읽기도 전혀 할 수 없음. 팔로워로부터 읽을 수는 있지만 데이터가 뒤쳐졌을 수 있음(비선형적)

- CAP 정리
=> CAP는 때때로 일관성(Consistency), 가용성(Availabirty), 분단 내성(Pariliont olerance) 중 두 개를 고르라는 것으로 표현됨
=> 네트워크 분단은 일종의 결함이므로 선택할 수 있는 원가가 아니기 때문에 
이러한 방식에는 문제의 여지가 있음
=> 네트워크 결함이 발생하면 선형성과 완전한 가용성 사이에서 선택해야 함
=> 따라서 CAP는 네트워크 분단이 생겼을 때 일관성과 가용성 중 하나를 선택하라는 의미로 보는 것이 좋음

- 선형성과 네트워크 지연
=> 선형성은 유용한 보장이지만 현실에서 실제로 선형적인 시스템은 드묾
=> 선형성은 네트워크 결함의 상황에서만이 아닌 항상 느림
=> 선형성을 원하면 읽기와 쓰기 요청의 응답시간이 적어도 네트워크 지연의 불확실성에 비례해야 함을 증명함
=> 선형성을 제공하는 더욱 빠른 알고리즘은 존재하지 않지만 완화된 일관성 모델은 훨씬 더 빠를 수 있음. 따라서 지연시간에 민감한 시스템에서는 트레이드오프가 중요함

 



3. 순서화 보장

 
3.1 순서화와 인과성

- 시스템이 인과성에 의해 부과된 순서를 지키면 그 시스템은 인과적으로 일관적이라고 함
=> 예를들어 스냅숏 격리는 인과적 일관성을 제공함. 데이터베이스에서 읽어서 데이터의 어떤 조각을 봤다면 그보다 인과적으로 먼저 발생한 어떤 데이터도 볼 수 있어야 함(도중에 그 데이터가 삭제되지 않았다고 가정할 때)

- 인과적 순서가 전체 순서는 아님
=> 선형성 데이터스토어에는 동시적 연산이 없음
=> 하나의 타임라인이 있고 모든 연산은 그 타임라인을 따라서 전체 순서가 정해져야 함
=> 처리되기를 기다리는 요청이 몇 개 있을 수는 있지만 데이터스토어는 동시성 없이 하나의 타임라인을 따라서 단일 데이터복사본에 연산을 실행해 모든 요청이 한 시점에 원자적으로 처리되도록 보장해줌

- 선형성은 인과적 일관성보다 강함
=> 시스템은 선형적으로 만드는 성능 손해를 유발하지 않고도 인과적 일관성을 만족시킬 수 있음
=> 인과적 일관성은 네트워크 지연 때문에 느려지지 않고 네트워크 장애가 발생해도 가용한 일관성 모델 중 가장 강한 것임

- 인과적 의존성 담기
=> 인과성을 유지하기 위해 어떤 연산이 어떤 다른 연산보다 먼저 실행됐는지 알아야 함
=> 인과적 순서를 결정하기 위해 데이터베이스는 애플리케이션이 데이터의 어떤 비전을 읽었는지 알아야 함


3.2 일련번호 순서화

- 인과성은 중요한 이론적개 념이지만 모든 인과적 의존성을 실제로 추적하는 것은 실용성이 떨어짐
=> 일련번호나 타임스탬프를 써서 이벤트의 순서를 정하는 방법이 있을 수 있겠음

- 램포트 타임스탬프
=> 인과성에 일관적인 일련번호를 생성하는 간단한 방법
=> 물리적 일 기준 시계와 아무 관련이 없지만 전체 순서화를 제공. 두 타임 스탬프가 있으면 카운가 큰 것이 타임스탬프가 큼. 카운터 값이 같으면 노드ID가 큰 것이 타임스탬프가 큼

- 타임스탬프 순서화로는 충분하지 않음
=> 램포트 타임스탬프가 인과성에 일관적인 연산의 전체 순서를 정의하지만 분산 시스템의 여러 공통 문제를 해결하는 데 아주 충분하지는 않음
=> 사용자명에 대한 유일성 제약조건 같은 것을 구현하려면 연산의 전체순서가 있는 것으로는 충분치 않음. 언제 그 순서가 확정되는지도 알아야 함. 사용자명을 생성하는 연산이 있고, 전체 순서상으로 그 연산보다 앞서는, 동일한 사용자명 획득 연산을 다른 어떤노드도 끼워넣을 수 없다면 그 연산을 성공으로 선언해도 안전함


3.3 전체 순서 브로드캐스트

- 전체 순서 브로드캐스트 사용하기
1) 데이터베이스 복제에 사용함
=> 모든 메시지가 데이터베이스에 쓰기를 나타내고 모든 복제 서버가 같은 쓰기 연산을 같은 순서로 처리하면 복제 서버들은 서로 일관성 있는 상태로 유지함
=> 이 원리를 상태 기계 복제라고 함

2) 직렬성 트랜잭션을 구현하는 데 사용함
=> 모든 메시지가 스토어드 프로시저로 실행되는 결정적 트렌재션을 나타낸다면, 그리고 모든 노드가 그 메시지들을 같은 순서로 처리한다면 데이터베이스의 파티션과 복제본은 서로 일관적인 상태를 유지함

3) 복제로그, 트랜젝션 로그나 쓰기 전 로그에서의 로그를 만드는 방법 중 하나임
=> 모든 노드가 같은 메시지를 같은 순서로 전달해야 하므로 모든 노드는 로그를 읽어서 순서가 동일한 메시지를 볼 수 있음

4) 팬싱 토근을 제공하는 잠금 서비스 구현 시에도 사용함
=> 잠금을 획득하는 모든 요청은 메시지로 로그에 추가되고 모든 메시지들은 로그에 나타난 순서대로 일련번호가 붙음. 그리면 일련번호는 단조 증가하므로 팬싱 토큰의 역할을 할 수 있음

- 전체 순서 브로드캐스트를 사용해 선형성 저장소 구현하기
=> 전체 순서 브로드캐스트를 추가 전용 로그로 사용해 선형성 compare-and-set 연산 구현하기
1) 메시자를 로그에 추가해서 점유하기 원하는 사용자명을 시험적으로 가리킴
2) 로그를 읽고, 추가한 메시지가 되돌아오기를 기다림
3) 원하는 사용자명을 점유하려고 하는 메시지가 있는지 확인. 원하는 사용자명에 해당하는 첫 번째 메시지가 자신의 메시지라면 성공한 것임. 사용자명 획득을 커밋하고 클라이언트에게 확인 응답을 보낼 수 있음. 원하는 사용자명에 해당하는 첫 번째 메시지가 다른 사용자가 보낸 것이라면 연산을 어보트시킴

- 선형성 저장소를 사용해 전체 순서 브로드캐스트 구현하기
=> 선형성 저장소가 있을 때 이를 기반으로 전체 순서 브로드캐스트를 구현하는 것도 가능함
=> 전체 순서 브로드캐스트를 통해 보내고 싶은 모든 메시지에 대해 선형성 정수로 increment- and-get 연산을 수행하고 레지스터에서 얻은 값을 일련번호로 메시지에 붙임. 그 후 메시지를 모든 노드에 보낼 수 있고(메시지가 손실되면 재전송) 수신자들은 일련번호 순서대로 메시지를 전달함

- 선형성 일련번호 생성기에 대한 고심은 합의 알고리즘으로의 도달을 이룸


4. 분산 트랜잭션과 합의

- 비공식적으로 합의의 목적은 단지 여러 노드들이 뭔가에 동의하게 만드는 것임

 


 
4.1 원자적 커밋과 2단계 커밋(2PC)

- 단일노드에서 분산 원자적 커밋으로
=> 단일 노드에서 트랜재선 커밋은 데이터가 디스크에 지속성있게 쓰여지는 순서에 결정적으로 의존함. 데이터가 먼저고 커밋 레코드는 그 다음임
=> 트랜잭션에 여러 노드가 관여한다면 다중 객체 트랜젝션을 쓰기나 용어 파티서닝된 보조 색인을 사용 할 수 있음
=> 모든 연산에 최대 카운터 값이 따라다니는 한 이 방법은 램포트 타임스탬프로부터 얻은 순서가 인과성에 일관적이도록 보장해줌. 모든 인과적 의존성이 타임스탬프를 증가시키기 때문임

- 2단계 커밋(2PC)
=> 2단계 커밋은 여러 노드에 걸친 원자적 트랜잭션 커밋을 달성하는 즉 모든 노드가 커밋되거나 모든 노드가 어보트되도록 보장하 는 알고리즘
=> 일부 데이터베이스에서는 2PC가 내부적으로 사용되고 XA 트랜잭션의 형태나 SOAP 웹 시비스용 Ws-AtomicTransaction을 통해 애플리케이션에서도 사용할 수 있음
=> 2PC는 단일 노드 트랜잭션에서는 보동 존재하지 않는 새로운 컴포넌트인 코디네이터(coorlinator, 트랜잭션 관리자라고도 함)를 사용
=> 2PC 트랜재션은 평상시처럼 애플리케이션이 여러 데이터베이스 노드에서 데이터를 읽고 쓰면서 시작함. 이런 데이터베이스 노 드를 트랜잭션의 참여자라고 부름 
=> 애플리케이션이 커밋할 준비가 되면 코디네이터가 1단계를 시작함. 각 노드에 준비 요청을 보내서 커밋할 수 있는지 물어봄. 그후 코디네이터는 참여자들의 응답을 추적함

- 약속에 관한 시스템
=> 프로토콜에는 두 개의 중대한 돌아갈 수 없는 지점이 있음
=> 참여자는" 네" 에 투표할 때 나중에 분명히 커밋할 수 있을 거라고 약속함(코디네이터는 여전히 어보트를 선택할 수는 있음). 코디네이터가 한 번 결정하면 그 결정은 변경할 수 없음
=> 이런 약속은 2PC의 원자성을 보장함(단일 노드 원자적 커밋은 이 두 개의 이벤트를 하나로 묶음. 트랜잭션 로그에 커밋 레코드 를 쓰는 일임)

- 코디네이터 장애
=> 참여자가 준비 요청을 받고 "네"에 투표했다면 더이상 일방적으로 어보트할 수 없음. 코디네이터로부터 트랜젝션이 커밋됐는지 어보트 됐는지 확신을 받을 때까지 기다려야 함
=> 코디네이터가 죽거나 이 시점에 네트워크에 장애가 나면 참여자는 기다릴 수 밖에 없음. 이 상태에 있는 참여자의 트랜젝션을 의심스럽다(in doubt) 또는 불확실하다(uncertain)고 함
=> 코디네이터가 참여자들에게 커밋이나 어보트 요청을 보내기 전에 디스크에 있는 트랜잭션 로그에 자신의 커밋이나 어보트 결정을 써야하는 이유임

- 3단계 커밋
=> 2단계 커밋은 2PC가 코디네이터가 복구하기를 기다리느라 멈출 수 있다는 사실 때문에 블로킹 원자적 커밋 프로토콜이라고 불림
=> 2PC의 대안으로 3단계 커밋(3PC)이라는 알고리즘이 제안됨
=> 3PC는 지연에 제한이 있는 네트워크와 응답시간에 제한이 있는 노드를 가정함. 기약없는 네트워크 지연과 프로세스 중단이 있는 대부분의 실용적 시스템에서 3PC는 원자성을 보장하지 못함
=> 일반적으로 논블로킹 원자적 커밋은 노드가 죽었는지 아닌지 구별할 수 있는 신뢰성 있는 매커니즘인 '완벽한 장애 감지기'가 필요함

 



4.2 현실의 분산 트랜잭션

- 정확히 한 번 메시지 처리
=> 이종 분산 트랜잭션은 다양한 시스템들이 강력한 방법으로 통합될 수 있게 함
=> 트랜잭션의 영향을 받는 모든 시스템이 동일한 원자적 커밋 프로토콜을 사용할 수 있을 때만 가능 

- XA 트랜잭션
=> W/Open XA(eXtended Architecture의 약자)는 이종 기술에 걸친 2단계 커밋을 구현하는 표준
=> XA는 네트워크 프로토콜이 아님. 트랜잭션 코디네이티와 연결되는 인터페이스를 제공하는 C API일 뿐임
=> 애플리케이션 프로세스가 죽거나 애플리케이션이 실행 중인 장비가 죽으면 코디네이터도 함께 사라짐. 그러면 준비됐지만 커밋되지 않은 트랜잭션들을 가진 참여자들은 의심스리운 상태에 빠지게 됨
=> 코디네이터의 로그는 애플리케이션 서버의 로컬 디스크에 있으므로 그 서버는 재시작돼야 하고 코디네이터 라이브러리가 그 로그를 읽어서 각 트랜잭션의 커밋/어보트 결과를 복구해야 함
=> 그후에야 코디네이터는 데이터베이스 드라이버의 XA콜백을 사용해 참여자들에게 직접 커밋하거나 어보트하라고 요청할 수 있음
=> 모든 통신이 클라이언트 라이브러리를 거쳐야 하므로 데이터베이스 서버는 코디네이터에 직접 연결할 수 없음

- 의심스러운 상태에 있는 동안 잠금을 유지하는 문제

=> 트랜잭션이 의심스러운 상태에 빠지는 것에 신경 쓰는 이유는 잠금과 관련이 있음
=> 데이터베이스는 트랜잭션이 커밋하거나 어보트할 때까지 이런 잠금을 해제할 수 없음
=> 잠금이 유지되는 동안 다른 어떤 트랜재션도 그 로우를 변경할 수 없음

- 코디네이터 장애에서 복구하기

=> 여러 XA 구현에는 참여자가 코디네이터로부터 확정적 결정을 얻지 않고 의심스러운 트랜잭션을 어보트하거나 커밋할지를 일방적으로 결정할 수 있도록 하는 경험적 결정(heuristic decision)이라고 부르는 비상 탈출구가 있음

- 분산 트랜잭션의 제약
=> XA 트랜젝션도 중요한 운영상 문제를 가져옴. 특히 핵심 구현은 트랜잭션 코디네이터 자체가(트랜잭션 결과를 저장할 수 있는) 일종의 데이터베이스여야 한다는 점이고 따라서 다른 중요한 데이터베이스와 동일하게 신경씨서 접근해야 함

 



4.3 내결함성을 지닌 합의

- 합의 알고리즘이 만족해야 하는 속성
1) 균일한 동의: 어떤 두 노드도 다르게 결정하지 않는다
2) 무결성: 어떤 노드도 두 번 결정하지 않는다
3) 유효성: 한 노드가 값 v를 결정한다면 v는 어떤 노드에서 제안된 것이다
4) 종료: 죽지 않은 모든 노드는 결국 어떤 값을 결정한다

- 합의 알고리즘과 전체 순서 브로드캐스트
=> 내결함성을 지닌 합의 알고리즘 중 가장 널리 알려진 것은 뷰스탬프 복제임(VSR)
=> 진체 순서 브로드캐스트를 하려면 모든 노드에게 메시지가 정확히 한 번, 같은 순서로 전달돼야 함
=> 각 회마다 노드들은 다음에 보내기 원하는 메시지를 제안하고 진체 순서상에서 전달될 다음 메시지를 결정함
=> 전체 순서 브로드캐스트는 합의를 여러번 반복하는 것과 동일함(각 합의 결정이 하나의 메시지 전달에 해당)

- 단일 리더 복제와 합의
=> 단일 리더 복제에서 합의에 대한 고려 여부는 리더를 어떻게 선택하느냐에 있음
=> 리더를 운영팀에 있는 사람이 수동으로 선택해서 설정한다면 본질적으로 독재자 방식의 합의 알고리즘을 사용하는 것임
=> 하지만 리더 선출을 위해서는 합의가 필요함. 합의를 해결하려면 먼저 합의를 해결해야 함

- 에포크 번호 붙이기와 정족수

=> 합의 프로토콜은 에포크번호를 정의하고 각 에포크 내에서는 리더가 유일하다고 보장함
=> 현재 리더가 죽었다고 생각될 때마다 새 노드를 선출하기 위해 노드 사이에서 투표가 시작됨
=> 리더가 뭔가를 결정하도록 허용하기 전에 충돌되는 결정을 할지도 모르는 애포크번호가 더 높은 다른 리더가 없는지 먼저 확인해야 함
=> 리더는 내리려고 하는 모든 결정에 대해 제안된 값을 다른 노드에게 보내서 노드의 정족수가 그 제안을 찬성한다고 응답하기를 기다려야 함. 정족수는 전형적으로 노드의 과반수로 구성됨
=> 따라서 두 번의 투표가 있음. 한 번은 리더를 선출하기 위해, 두 번째는 리더의 제안에 투표하기 위해서임
=> 중요한 것은 두 번의 투표를 하는 정족수가 겹쳐야 한다는 점임
=> 이 투표 과정은 겉보기에는 2단계 커밋과 비슷해 보이지만 가장 큰 차이는 이러함
1) 2PC에서 코디네이터는 선출되지 않는다는 것
2) 2PC는 모든 참여자로부터 "네" 투표가 필요하지만 내결함상을 지닌 합의 알고리즘은 노드의 과반수로부터만 투표를 받으면 된다는 것

- 합의의 제약

=> 제안이 결정되기 전에 노드가 제안에 투표하는 과정은 일종의 동기식 복제임. 이런 설정에서 커밋된 데이터는 장애 복구 시 잠재적으로 손실될 수 있음
=> 합의 시스템은 항상 엄격한 과반수가 동작하기를 요구함. 노드 한 대의 장애를 견디기 위해 최소 3대의 노드가 필요하다는 의미임
=> 대부분의 합의 알고리즘은 투표에 참여하는 노드 집합이 고정돼 있다고 가정하며 이는 클리스터에 노드를 그냥 추가하거나 제지할 수 없다는 의미임
=> 합의 시스템은 장에노드를 감지하기 위해 일반적으로 타임아웃에 의존함. 네트워크 지연의 변동이 심한 환경에서 일시적인 네트워크 문제 때문에 노드가 리더에 장애가 발생했다고 잘못 생각하는 일이 종종 생김
=> 때때로 합의 알고리즘은 네트워크 문제에 특히 민감함


4.4 멤버십과 코디네이션 서비스

- 작업을 노드에 할당하기
=> 애플리케이션은 처음에는 단일 노드에서만 실행될지 모르지만 결국에는 수천대의 노드로 늘어날 수도 있음
=> 매우 많은 노드에서 과반수 투표를 수행하려고 하는 것은 지독하게 비효율적임
=> 대신 주키퍼는(보통 셋이나 다섯의) 고정된 수의 노드에서 실행되고 이 노드들 사이에서 과반수 투표를 수행하면서 많아질 수 있는 클라이언트를 지원함
=> 따라서 주키퍼는 노드들을 코디네이트하는 작엄(합의, 연산순서화, 장애감지)의 일부를 외부 서비스에 위탁하는 방법을 제공함

- 서비스 찾기
=> 주키퍼. cIcd, 콘술(Consul)은 서비스 찾기(service discovery), 즉 특정 서비스에 연결하려면 어떤 IP 주소로 접속해야 하는지 알아내는 용도로도 자주 사용됨
=> 서비스 찾기는 합의가 필요없지만 리더선출은 합의가 필요함
=> 따라서 합의 시스템이 누가 리더인지 이미 안다면 다른 서비스들이 리더가 누구인지 찾는 데 그 정보를 사용하는 것도 타당함
=> 이런 목적으로 어떤 합의시스템은 읽기전용 캐시 복제서버를 지원함

- 멤버십 서비스
=> 멤버십 서비스는 클러스터에서 어떤 노드가 현재 활성화된 살아있는 멤버인지 결정함
=> 기약없는 네트워크 지연 때문에 다른 노드에 장애가 생겼는지 신뢰성있게 감지하는 것은 불가능함
=> 그러나 장애감지를 합의와 연결하면 노드들은 어떤 노드가 살아있는 것으로 여겨져야 하는지 혹은 죽은 것으로 여겨져야 하는지에 동의할 수 있음