9. 일관성과 합의
- 8장에서 봤듯이 분산 시스템에서는 많은 것들이 잘못될 수 있습니다. 쉬운 해결책으로 전체 서비스가 실패하도록 두고 사용자에게 오류 메시지를 보여주는 것이빈다.
- 이러한 해결책이 어렵다면 결함을 견더낼(tolerating) , 방법을 찾아야합니다.
- 아래에서는 내결함설을 지닌 분산 시스템을 구축하는 데 쓰이는 알고리즘과 프로토콜의 몇 가지 예를 이야기합니다.
#
일관성 보장- 복제 데이터 베이스는 대부분 최소한 최종적 일관성을 제공합니다.
- 데이터베이스에 쓰기를 멈추고 볼 특정 시간 동안 기다리면 결국 모든 읽기 요청이 같은 값을 반환한다는 뜻이빈다.
- 모든 복제본이 결국 같은 값으로 수렴되기를 기대함으로 최종적 일관성보다 수렴이 더 나은 이름일 수도 있습니다.
- 다만 위는 매우 약한 보장이며, 언제 복제본이 수렴될지에 대해서는 이야기가 없습니다.
- 약한 보장만 제공하는 데이터베이스를 다룰 때는 그 제한을 알아야하고 뜻하지 않게 너무 많은 것을 가정하면 안됩니다.
아래에서는 데이터 시스템이 선택적으로 제공할 수 있는 더욱 강한 일관성 모델을 살펴봅니다.
- 공통적으로 사용되는 가장 강한 일관성 모델 중 하나인 선현성(linearizability)을 살펴보고 장점과 단점을 검토합니다.
- 분산 시스템에서 이벤트 순서화 문제, 특히 인과성과 전체 순서화와 관련된 문제를 검토합니다.
- 분산 트랜잭션을 원자적으로 커밋하는 방법을 살펴봅니다.
#
선형성- 최종적 일관성을 지닌 데이터베이스에서 두 개의 다른 복제본의 같은 질문을 동시에 하면 두 가지 다른 응답을 받을 수도 있습니다.
- 위 문제에서 데이터베이스가 복제본이 하나만 있다면 더 단순해지지 않을까라는 생각을 할 수 있으며, 이는 선형성을 뒷받침해주는 아이디어입니다.
- 선형성은 원자적 일관성(atomic consistency), 강한 일관성(strong consistency), 즉각 일관성(immediate consistency), 외부 일관성(external consistency) 라고 합니다.
- 기본적인 아이디어는 시스템에 데이터 복사본이 하나만 있고 그 데이터를 대상으로 수행하는 모든 연산은 원자적인 것처럼 보이게 만드는 것입니다.
- 선형성 시스템에서는 클라이언트가 쓰기를 성공적으로 완료하자마자 그 데이터베이스를 읽는 모든 클라이언트는 방금 쓰여진 값을 볼 수 있어야 합니다.
- 즉, 선형성은 최신성 보장(recency guarantee) 를 의미합니다.
#
시스템에 선형성을 부여하는 것은 무엇인가?- 선형성을 받침하는 기본 아이디어는 시스템에 데이터 복사본이 하나뿐인 것처럼 보이게 만드는 것입니다.
- 선현성 데이터베이스에서 동시에 같은 키 x를 읽고 쓰는 세 클라이언트는 분산 시스템에 분야에서는 x를 레지스터(register) 라고 부릅니다.
- 이 예제에서는 레지스터는 두 가지 종류의 연산이 있습니다.
read(x) => v
는 클라이언트가 레지스터 읽기를 요청하고 데이터베이스가 값 v를 반환했다는 것을 의미합니다.write(x, v) => r
은 클라이언트가 레지스터 x의 값을 v로 설정하라고 요청했고 데이터베이스가 응답 r을 반환했다는 것을 의미합니다.
- 읽기 연산이 처리되는 시점에 쓰기의 영향이 발생했는지 알 수 없으므로, 쓰기와 읽기 연산은 동시에 실행됩니다.
- 이를 해결하기 위해서는 다른 제약 조건을 추가해야합니다.
- 이를 좀 더 개선한 경우는 다음과 같습니다. (읽기와 쓰기 외에 세 번째 종류의 연산을 추가합니다.)
Client B
의 마지막 읽기는 선형적이지 않습니다.- 이러한 방법을 통해서 모든 요청과 응답 시점을 유효한 순차 순서로 배열되는지 확인함으로써 시스템의 동작이 선형적인지 테스트할 수 있습니다.
선형성 대 직렬성
- 직렬성
- 직렬성은 모든 트랜잭션이 여러 객체를 읽고 쓸 수 있는 상황에서의 트랜잭션들의 격리 속성입니다.
- 직렬성은 트랜잭션들이 어떤 순서에 따라 실행되는 것처럼 동작하도록 보장합니다.
- 선형성
- 레지스터(개별 객체)에 실행되는 읽기와 스기에 대한 최신성 보장입니다.선형성은
- 선형성은 연산을 트랜잭션으로 묶지 않아서 충돌 구체화 같은 부가적인 수단을 사용하지 않으면 쓰기 스큐 같은 문제를 막지 못합니다.
데이터베이스는 직렬성과 선형성을 모두 제공할 수 있으므로 이러한 조합은 엄격한 직렬성(strict serializability) 혹은 강한 단일 복사 직렬성(strong one-copy serializability) 라고 합니다.
그러나 직렬성 스냅숏 격리는 선형적이지 않습니다. 설계상 직렬성 스냅숏 격리는 읽는 쪽과 쓰는 쪽 사이의 잠금 경쟁을 피하기 위해 일관된 스냅숏에서 읽습니다. 일관된 스냅숏의 요점은 스냅숏에 스냅숏보다 나중에 실행된 쓰기를 포함하지 않는다는 것이고 따라서 스냅숏 읽기는 선형적이지 않습니다.
#
선형성에 기대기선형성이 중요한 요구사항이 되는 경우가 때때로 있습니다.
#
잠금과 리더 선출- 단일 리더 복제를 사용하는 시스템은 리더가 여러개(스플릿 브레인)가 아니라 진짜 하나만 보장하도록 해야합니다.
- 분산 잠금과 리더 선출을 구현하기 위해 아파치 주키퍼(Apache ZooKeepr)나 etcd 같은 코디네이션 서비스가 종종 사용됩니다.
- 이들은 합의 알고리즘을 사용해 선형성 연산을 내결함성이 있느 방식으로 구현합니다.
- 분산 잠금은 오라클 리얼 애플리케이션 클러스터 같은 분산 데이터베이스에서 훨씬 세분화된 수준으로 사용되기도 합니다.
#
제약 조건과 유일성 보장- 유일성 제약 조건은 데이터베이스에서 흔합니다. (Ex. 사용자 명이나 이메일 주소)
- 이러한 조건을 강제하고 싶다면 선형성이 필요합니다.
- 마찬가지로, 은행 계좌 잔고가 음수가 되지 않거나 예약의 경우 모든 노드가 동의하는 하나의 최신 값이 있기를 요구합니다.
- 실제 애플리케이션에서는 종종 이런 제약 조건을 느슨하게 다르기도 하지만, 관계형 데이터베이스에서 전형적으로 볼 수 있는 엄격한 유일성 제약 조건은 선현성이 필요합니다.
#
채널 간 타이밍 의존성- 파일 저장 서비스가 선형적이면 잘 돌아가지만, 아니라면 경쟁 조건의 위험이 언제나 있습니다.
- 선형성이 경쟁 조건을 회피하는 유일한 방법은 아니지만 이해하기에 가장 단순합니다.
#
선형성 시스템 구현하기이전의 내용에서 선형적이 될 가능성을 확인해보면 아래와 같습니다.
- 단일 리더 복제 : 선형적 가능성 존재
- 읽기에 리더를 사용하기 위해서는 누가 리더인지를 정확하게 안다고 가정해야 합니다.
- 합의 알고리즘 : 선형적
- 주키퍼 등의 코디네이트를 통해서 가능합니다.
- 다중 리더 복제 : 비선형적
- 여러 노드에서 동시 쓰기를 처리하고 쓰여진 내용을 비동기적으로 처리하기 때문에 선형적이지 않습니다.
- 리더 없는 복제 : 아마도 비선형적
- 정족수 및 엄격한 일관성에 따라 결정됩니다.
#
선형성과 정족수- 엄격한 정족수를 사용한 읽기 쓰기는 선형적인 것처럼 보입니다. 그러나 네트워크 지연의 변동이 심하면 경쟁 조건이 생길 수 있습니다.
- 정족수 조건이 만족됨에도 실행은 선형적이지 않을 수 있습니다.
- 성능이 떨어지는 비용을 지불하고 다이타모 스타일 정족수를 선형적으로 만드는 것도 가능합니다.
- 다만 이경우는 선형성 읽기와 쓰기 연산만 수행할 수 있습니다.
- 즉, 다이나모 스타일 복제를 하는 리더 없는 시스템은 선형성을 제공하지 않는다고 보는 게 안전합니다.
#
선형성의 비용- 두 데이터센터 사이에 네트워크가 끊기면 다중 리더 데이터베이스는 사용이 가능하지만 단일 리더 복제의 경우는 리더가 데이터센터 중 하나에 있어야만 돌아갑니다.
- 즉, 팔로워 데이터센터로 접속한 클라이언트는 리더를 연결할 수 없으므로 데이터베이스 쓸 수 없고 선형성 읽기도 전혀 할 수 없습니다.
#
CAP 정리- 위 문제는 다른 경우에도 발생할 수 있습니다.
- 애플리케이션에서 선형성을 요구하고 네트워크 문제 때문에 일부 복제 서버가 다른 복제 서버와 연결이 끊기면 일부 복제 서버는 연결이 끊긴 동안은 요청을 처리할 수 없습니다. 네트워크 문제가 고쳐질 때까지 기다리거나 오류를 반환해야 합니다. (즉, 가용성이 없습니다.)
- 애플리케이션에서 선형성을 요구하지 않는다면 각 복제 서버가 다른 복제 서버와 연결이 끊기더라도 독립적으로 요청을 처리하는 방식으로 쓰기를 처리할 수 있습니다. 이 경우 애플리케이션은 네트워크 문제에 직면해도 가용한 상태를 유지하지만 그 동작은 선형적이지 않습니다.
- 선형성이 필요 없는 애플리케이션은 네트워크 문제에 더 강인합니다.
- 이는 CAP 정리로 알려졌습니다.
- CAP는 데이터베이스에서 트레이드오프에 대한 논의를 시작하려는 목적으로 정확한 정의 없이 경험 법칙으로 제안되었습니다.
- 공식적으로 정의된 CAP 정리는 매우 범위가 좁으며, 오직 하나의 일관성 모델과 한 종류의 결함만 고려합니다.
- 더 이상은 실용적인 가치는 없습니다.
도움이 안되는 CAP 정리
- CAP는 때때로 일관성(Consistency), 가용성(Availability), 분단 내성(Partition tolerance) 이라는 세 개 중 두 개를 고르라는 것으로 표현됩니다.
- CAP는 네트워크 분단이 생겼을 때 일관성과 가용성 중 하나를 선택하라는 의미로 보는 것이 좋습니다.
- 또한 가용성이란 단언느 몇 가지 모순된 정의가 있습니다.
#
선형성과 네트워크 지연- 선형성은 유용한 보장이지만 현실에서는 실제로 선형적인 시스템은 놀랄만큼 드뭅니다.
- 성능을 위해 선형성이 손실되는 트레이드 오프가 발생하는 경우가 자주 발생합니다.
- 분산 데이터베이스도 마찬가지의 이유 입니다.
- 즉, 선형성을 원하면 읽기와 쓰기 요청의 응답 시간이 적어도 네트워크 지연의 불확싱성에 비례합니다.
- 지연시간에 민감한 시스템에서는 이러한 트레이드 오프가 중요합니다.
아래에서는 정확성을 희생하지 않고 선형성을 회피하는 방법입니다.
#
순서화 보장선형성 레지스터는 데이터 복사본이 하나만 있는 것처럼 동작하고 모든 연산이 어느 시점에서 원자적으로 효과가 나타나는 것처럼 보이며 이는 순서화라고 표현할 수 있습니다.
- 단일 리더 복제에서 리더의 주 목적은 복제 로그에서 쓰기의 순서를 결정하는 것입니다.
- 직렬성은 트랜잭션들이 어떤 일련 순서에 따라 실행되는 것처럼 동작하도록 보장하는 것과 관련되어 있습니다.
- 분산 시스템에서 타임 스탬프와 시계 사용은 무질서한 세상에서 질의를 부여하는 또 다른 시도입니다.
순서화, 선형성, 합의 사이에는 깊은 연결 관계가 있음을 드러냅니다.
#
순서화와 인과성순서화는 인과성을 보존하는데 도움을 줍니다.
- 일관된 순서로 읽기에서 대화의 관찰자가 질문에 대한 응답을 먼저 보고 나서 응답된 질문을 보게되는 예를 보았는데, 이러한 상황을 질문과 답변 사이에 인과적 의존성(causal dependency)가 있다고 말합니다.
- 동시 쓰기 감지에서 두 개의 연산 A와 B가 있으면 세 가지 가능성이 있을 수 있습니다. A가 B보다 먼저, 같이, 이후에 실행되는 경우가 있는데 이처럼 이전 발생(happened before) 관계는 인과성을 표현하는 또 다른 방법입니다.
- 트랜잭션을 스냅숏 격리의 맥락에서 트랜잭션은 일관된 스냅숏으로부터 읽는다고 했습니다. 이때 일관적이란 인과성에 일관적(consistent with causality) 라는 의미입니다.
인과성은 이벤트에 순서를 부과합니다. 결과가 나타나기 전에 원인이 발생하고, 메시지를 받기 전에 메시지를 보냅니다.
시스템이 인과성에 의해 부과된 순서를 지키면 그 시스템은 인과적으로 일관적(causally consistent) 라고 합니다.
#
인과적 순서가 전체 순서는 아니다- 전체 순서(total order)은 어떤 두 요소를 비교할 수 있게 하므로 두 요소가 있으면 항상 어떤 것이 더 크고 어떤 것이 더 작은지 말할 수 잇습니다.
- 수학적 집합은 이와 달리 비교불가(incomparable)하고, 부분적으로 순서가 정해집니다(partially ordered).
- 전체 순서와 부분 순서의 차이점은 다른 데이터베이스 일관성 모델에 반영됩니다.
- 선형성, 선형성 시스템에서는 연산의 전체 순서를 정할 수 있습니다.
- 인과성, 두 연산 중 다른 것뽀다 먼저 실행되지 않았다면 두 연산이 동시적이라고 말했습니다. 인과성이 전체 순서가 아닌 부분 순서를 정의한다는 이야기 입니다.
- 이러한 정의를 통해서 선형성 데이터스토어는 동시적 연산이 없습니다. 하나의 타임라인이 있고 모든 연산은 그 타임라인을 따라서 전체 순서가 정해져야 합니다.
- 동시성은 타임라인이 갈라졌다 다시 합쳐지는 것을 의미합니다.
- 깃의 히스토리는 인과적 의존성 그래프와 매우 유사합니다.
#
선형성은 인과적 일관성보다 강하다- 선형성은 인과성을 내포합니다.
- 선형성은 인과성을 보장해준다는 사실은 이해하기도 쉽고 매력적으로 보일 수도 있지만, 이는 네트워크 지연에 따라 성능과 가용성에 해가 될 수 있습니다.
- 다만 절충선으로 선형적으로 만들지 않고도 인과적 일관성을 만족시킬 수 있습니다.
- 대부분의 시스템에서 실제로 필요한 것은 선형성이 아닌 인과성입니다.
#
인과적 의존성 담기- 인과성을 유지하기 위해서는 어떤 연산이 어떤 다른 연산보다 먼저 실행됐는지 알아야 합니다.
- 인과적 의존성을 결정하려면 시스템에 있는 노드에 관한 "지식"을 기술할 방법이 필요합니다.
- 앞에서는 리더 없는 데이터스토어는 갱신 손실 방지를 위해 같은 키에 대한 동시 쓰기를 검출해야하며, 인과적 의존성은 여기서 더 나아가서 버전 벡터(version vector)를 일반화할 수 있습니다.
#
일련번호 순서화- 인과성은 중요한 이론적 개념이지만 모든 인과적 의존성을 추적하는 것은 실용성이 떨어집니다.
- 더 좋은 방법으로는 일련번호나 타임스탬프를 써서 이벤트의 순서를 정할 수 있습니다.
- 물리적 시계에서 얻을 필요 없이 논리적 시계에서 얻으면 됩니다.
- 이러한 일련번호나 타임스탬프는 크기가 작고 전체 순서를 제공합니다.
- 특히 인과성에 일관적인 전체 순서대로 일련번호를 생성할 수 있습니다.
- 연산 A가 연산 B보다 인과적으로 먼저 실행되었다면 A는 전체 순서에서도 B보다 먼저입니다.
- 단일 리더 복제를 쓰는 데이터베이스에서는 복제 로그가 인과성에 일관적인 쓰기 연산의 전체 순서를 정의합니다.
- 리더는 연산마다 카운터를 증가시키고 복제 로그의 연산에 단조 증가하는 일련번호를 할당하기만 하면 됩니다.
#
비인과적 일련번호 생성기- 단일 리더가 없다면 연산에 사용할 일련번호를 생성하는 방법이 명확해 보이지 않습니다. 현실에서는 다양한 방법이 사용됩니다.
- 각 노드가 자신만의 독립적인 일련번호 집합을 생성할 수 있습니다. (몇 비트를 예약해서 고유 노드 식별자를 포함하는 방법입니다.)
- 각 연산에 일 기준 시계에서 얻은 타임스탬플르 붙일 수 있습니다.
- 일련번호 블록을 미리 할당할 수 있습니다.
- 위의 세가지 선택지는 카운터를 증가하는 단일 리더에 모든 연산보다는 확장성이 좋으나, 생성된 일련번호가 인과성에 일관적이지 않습니다. (근사적)
- 아래의 인과성 문제점이 발생할 수 있습니다.
- 각 노드는 초당 연산수가 다를 수 있습니다.
- 물리적 시계에서 얻은 타임스탬프는 시계 스큐에 종속적입니다.
- 블록 할당자의 경우, 한 연산이 1001과 2000사이의 구간에서 일련번호를 받고 인과적으로 나중에 실행되는 연산이 1과 1000사이의 구간에서 일련번호를 받을 수 있습니다.
#
램포트 타임스탬프- 이러한 방법 대신 인과성에 일관적인 일련번호를 생성하는 간단한 방법이 있으며 이를 램포트 타임스탬프(Lamport timestamp) 라고 부릅니다.
- 램포트 타임스탬프는 (카운터, 노드ID) 의 쌍입니다.
- 램포트 타임스탬프는 물리적 일 기준 시계와 아무 관련이 없으나 전체 순서화를 제공합니다.
- 두 타임 스탬프가 있으면 카운터가 큰 것이 타임스탬프가 크며, 카운터 값이 같으면 노드 ID가 큰 것이 타임 스탬프가 큽니다.
- 핵심 아이디어는 모든 노드와 모든 클라이언트가 본 카운터 값 중 최댓값을 추적하고 모든 요청에 그 최댓값을 포함합니다.
- 램포트 타임스탬프는 버전 벡터와 혼동될 수 있지만 목적이 다릅니다.
- 버전 벡터는 두 연산이 동시적인지 또는 어떤 연산이 다른 연산에 인과적으로 의존하는지 구별할 수 있지만 램포트 타임스탬프는 항상 전체 순서화를 강제합니다.
#
타임스탬프 순서화로는 충분하지 않다- 램포트 타임스탬프가 인과성에 일관적인 연산의 전체 순서를 정의하지만 분산 시스템의 공통 문제를 해결하는 데 아주 충분하지는 않습니다.
- 램포트 타임스탬프는 사후에 성공하는 쪽을 결정하는 데는 효과적입니다. 시스템에서 사용자명 생성 연산을 모두 모으면 그들의 타임스태프를 비교할 수 있습니다.
- 그러나 노드가 사용자로부터 사용자명 생성 요청을 막받고 그 요청이 성공해야 하는지 실패해야 하는지 당장 결정해야 할 땐느 이 방법으로 부족합니다.
- 다른 어떤 노드도 동시에 더 낮은 타임스탬프를 가지고 동일한 사용자명으로 계정 생성을 처리하는 중이 아니라고 확신하려면 다른 노드가 무엇을 하고 있는지 확인해야 하며, 다른 노드 중 하나에 장애가 생기거나 네트워크 문제 때문에 연결할 수 없다면 시스템이 멈추게 됩니다.
- 즉, 연산의 전체 순서는 모든 연산은 모은 후에야 드러납니다
- 결론적으로, 사용자명에 대해 유일성 제약 조건 같은 것을 구현하려면 연산의 전체 순서가 있는 것으로는 충분하지 않습니다.
- 이를 해결하는 아이디어로 전체 순서 브로드캐스트가 등장했습니다.
#
전체 순서 브로드캐스트단일 리더 복제는 한 노드를 리더로 선택하고 리더의 단일 CPU 코어에서 모든 연산을 차례대로 배열함으로써 연산의 전체 순서를 정합니다. 분산 시스템 분야에서 이 문제는 전체 순서 브로드캐스트(total order broadcast) 나 원자적 브로드 캐스트(atomic broadcast) 로 알려져 있습니다.
전체 순서 브로드캐스트는 보통 노드 사이에 메시지를 교환하는 프로토콜로 기술되며 아래의 두 가지 안전성 속성을 항상 만족해야 합니다.
- 신뢰성 있는 전달(reliable delivery)
- 어떤 메시지도 손실되지 않습니다. 메시지가 한 노드에 전달되면 모든 노드에도 전달됩니다.
- 전체 순서가 정해진 전달(Totally ordered delivery)
- 메시지는 모든 노드에 같은 순서로 전달됩니다.
전체 순서 브로드캐스트를 구현하는 올바른 알고리즘은 노드나 네트워크에 결함이 있다라도 신뢰성과 순서화 속성이 항상 만족되도록 보장해야 합니다.
#
전체 순서 브로드캐스트 사용하기- 주키퍼나 etcd 같은 합의 서비스는 전체 순서 브로드캐스트를 실제로 구현합니다.
- 모든 메시지가 데이터베이스의 쓰기를 나타내고 모든 복제 서버가 같은 쓰기 연산을 같은 순서로 처리하면 복제 서버들은 서로 일관성 있는상태를 유지합니다. 이 원리를 상태 기계 복제(state machine replication) 이라고 합니다.
- 전체 순서 브로드캐스트는 직렬성 트랜잭션을 구현하는데도 쓸 수 있습니다.
- 모든 노드가 메시지들을 같은 순서로 처리한다면 데이터베이스의 파티션과 복제본은 서로 일관적인 상태를 유지합니다.
- 전체 순서 브로드캐스트의 중요한 특면은 메시지가 전달되는 시점에 그 순서가 고정됩니다.
- 이 때문에 전체 순서 브로드캐스트가 타임스탬프 순서화보다 강합니다.
- 전체 순서 브로드캐스트를 보는 또 다른 관점은 로그를 만드는 방법 중 하나입니다.
- 전체 순서 프로드캐스트는 펜싱 토큰을 구현하는데도 유현합니다.
#
전체 순서 브로드캐스트를 사용해 선형성 저장소 구현하기- 전체 순서 브로드캐스트는 비동기식입니다.
- 메시지는 고정된 순서로 신뢰성 있게 전달되도록 보장되지만 언제 메시지가 전달될지는 모장되지 않습니다.
- 전체 순서 브로드캐스트 구현을 기반으로 선형성 저장소를 만들 수 있습니다.
- compare-and-set 연산을 다음과 같이 구현할 수 있습니다.
- (1) 메시지를 로그에 추가해서 점유하기 원하는 사용자명을 시험적으로 가리킵니다.
- (2) 로그를 읽고, 추가한 메시지가 되돌아오기를 기다립니다.
- (3) 원하는 사용자명을 점유하려고 하는 메시지가 있는지 확인합니다.
- 위 절차는 선형성 쓰기를 보장하지만 선형성 읽기는 보장하지 않습니다. 즉, 비동기로 갱신되는 저장소를 읽으면 오래된 값이 읽힐 수 있습니다. (타임라인 일관성 이라 부릅니다.)
- 읽기를 선형적으로 만들려면 아래의 선택지가 있습니다.
- 로그를 통해 순차 읽기를 할 수 있습니다. 고르에 메시지를 추가하고 로그를 읽어서 메시지가 되돌아왔을 때 실제 읽기를 수행하면 됩니다.
- 로그에서 최신 로그 메시지의 위치를 선형적 방법으로 얻을 수 있다면 그 위치를 질의하고 그 위치까지의 모든 항목이 전달되기를 기다린 후 읽기를 수행할 수 있스빈다.
- 쓰기를 실행할 때 동기식으로 갱신돼서 최신이 보장되는 복제 서버에서 읽으 수 있습니다.
#
선형성 저장소를 사용해 전체 순서 브로드캐스트 구현하기- 위에서는 전체 순서 브로드캐스트로부터 선형성 compare-and-set 연산을 구현하는 방법이었으나 이 반대도 가능합니다.
- 가장 쉬운 방법은 정수를 저장하고, 원자적 increment-and-get 연산이 지원되는 선형성 레지스터가 있다고 가정하는 것입니다.
- 이 연산을 통해 레지스터에서 얻은 값을 일련번호로 베시지에 붙입니다.
- 메시지를 모든 노드에 보낼 수 있고 수신자들은 일련번호 순서대로 메시지를 전달합니다.
- 핵심은 선형성 compare-and-set(혹은 increment-and-get) 레지스터와 전체 순서 브로드캐스트는 둘 다 합의와 동등하다고(equivalent to consensus) 증명할 수 있습니다.
Q. 주키퍼 쓰는지
#
분산 트랜잭션과 합의합의는 분산 컴퓨팅에서 가장 중요하고 근본적인 문제 중 하나이며 목적은 여러 노드들이 뭔가에 동의하게 만드는 것입니다.
노드가 동의하는 것이 중요한 상황은 아래와 같이 있습니다.
- 리더 선출, 단일 리더 복제를 사용하는 데이터베이스에서는 모든 노드는 어떤 노드가 리더인지를 동의해야 합니다.
- 원자적 커밋, 트랜잭션 원자성을 유지하고 싶다면 모든 노드가 트랜잭션의 결과에 동의하게 만들어야 합니다.
합의 불가능성
- FLP는 어떤 노드가 죽을 위험이 있다면 항상 합의에 이를 수 있는 알고리즘은 없습니다.
- 합의 불가능성에 대한 FLP 결과는 이론적으로 매우 중요하지만 분산 시스템은 보통 현실에서 합의를 달성할 수 있습니다.
#
원자적 커밋과 2단계 커밋(2PC)- 트랜잭션 원자성의 목적은 여러 쓰기를 실행하는 도중 뭔가 잘못되는 경우에 간단한 시맨틱을 제공하기 위함입니다.
- 트랜잭션의 결과는 커밋 성공이나 어보트입니다.
- 원자성은 실패한 트랜잭션이 절반만 완료된 결과나 절반만 갱신된 상태로 데이터베이스를 어지럽히는 것을 막아줍니다.
- 이는 다중 객체 트랜잭션과 보조 색인을 유지하는 데이터베이스에서 특히 중요합니다.
- 원자 색인은 보조 색인이 주 데이터와 일관성을 유지하도록 보장합니다.
#
단일 노드에서 분산 원자적 커밋으로- 단일 데이터베이스 노드에서 실행되는 트랜잭션에게 원자성은 흔히 저장소 엔진에서 구현됩니다.
- 단일 노드에서 트랜잭션 커밋은 데이터가 디스크에 지속성 있게 쓰여지는 순서에 결정적으로 의존합니다.
- 커밋을 원자적으로 만들어주는 것은 단일 장치입니다.
- 트랜잭션에 여러 노드가 관여되면 어떤 노드에서는 커밋이 성공하고 다른 노드에서는 실패해서 원자성 보장을 위반하기 쉽습니다.
- 어떤 노드들은 제약 조건 위반이나 충도을 가짐해서 어브가 필요하게 되지만 다른 노드들은 성공적으로 커밋될 수 있습니다.
- 어떤 커밋 요청은 네트워크에서 손실되어 타임아웃 때문에 결국 어보트되지만 다른 커밋 요청은 전달될 수 있습니다.
- 어떤 노드는 커밋 레코드가 완전히 쓰여지기 전에 죽어서 복구할 때 롤백되지만 다른 노드는 성공적으로 커밋될 수 있습니다.
- 어떤 노드가 트랜잭션을 커밋하지만 다른 노드는 어보트한다면 노드들이 서로 일관성이 없어집니다.
- 트랜잭션 커밋은 되돌릴 수 없어야합니다. 이 원칙은 "커밋 후 읽기"에서 설명한 커밋 후 읽기 격리의 기반을 형성합니다.
- 커밋된 트랜잭션의 효과를 나중에 다른 보상 트랜잭션(compensating transaction) 이 취소하는 것은 가능합니다. (다만 이는 분리된 트랜잭션 개념으로 이해해야 합니다.)
#
2단계 커밋 소개- 2단계 커밋은 여러 노드에 걸친 원자적 트랜잭션 커밋을 달성하는, 즉 모든 노드가 커밋되거나 모든 노드가 어보트되도록 보장되는 알고리즘입니다.
- 일부 데이터베이스는 2PCV가 내부적으로 사용되고 XA 트랜잭션의 형태나 SOAP 웹 서비스용 WS-AtomicTransaction을 통해 애플리케이션에서 사용할 수 있습니다.
- 2PC는 단일 노드 트랜잭션에서는 보통 존재하지 않느 새로운 컴포넌트인 코디네이터(coordinator, 트랜잭션 관리자)를 사용합니다.
- 2PC 트랜잭션에서 데이터베이스 노드를 트랜잭션의 참여자(participant) 라고 부릅니다. 애플리케이션이 커밋할 준비가 되면 코디네이터가 1단계를 시작하고, 각 노드에 준비 요청을 보내서 커밋할 수 있는지 물어봅니다. 그 후 코디네이터는 참여자들의 응답을 추적합니다.
- 모든 참여자가 커밋할 준비가 됐다는 뜻으로 "네"라고 응답하면 코디네이터는 2단계에서 커밋 요청을 보내고 커밋이 실제로 일어납니다.
- 함여자 중 누구라도 "아니오"로 응답하면 코디네이터는 2단계에서 모든 노드에 어보트 요청을 보냅니다.
#
약속에 관한 시스템- 2PC의 경우, 두 개의 중대한 "돌아갈 수 없는 지점"이 있습니다.
- 참여자는 "네"에 투표를 했을 때 나중에 분명히 커밋할 수 있을 것이라 약속합니다.
- 코디네이터가 한 번 결정하면 그 결정은 변경할 수 없습니다.
- 이러한 약속으로 2PC의 원자성을 보장합니다.
#
코디네이터 장애- 코디네이터가 준비 요청을 보내기 전에 장애가 나면 참여자가 안전하게 트랜잭션을 어보트할 수 있습니다.
- 코디네이터로부터 트랜잭션이 커밋됐는지 회신을 받는 와중, 코디네이터가 죽거나 이 시점에 네트워크 장애가 나면 참여자는 기다릴 수 밖에 없으며 이 트랜잭션을 의심스럽다(in doubt) 혹은 불확실하다(uncertain) 라고 합니다.
- 2PC가 완료할 수 있는 유일한 방법은 코디네이터가 복구되기를 기다리는 것 뿐입니다.
- 이때문에 코디네이터가 참여자들에게 커밋이나 어보트 요청을 보내기 전에 디스크에 있는 트랜잭션 로그에 자신의 커밋이나 어보트 결정을 써야하는 이유입니다.
#
3단계 커밋- 2단계 커밋은 2PC가 코디네이터가 복구하기를 기다리느라 멈출 수 있다는 사실 때문에 블로킹 원자적 커밋 프로토콜이라고 부릅니다.
- 2PC의 대안으로 3단계 커밋(3PC)이라는 알고리즘이 제안되었습니다.
- 논블로킹 원자적 커밋은 완벽한 장애 감지기(perfect failure detector) ,노드가 죽었는지 아닌지 구별할 수 있는 신뢰성 있는 메커니즘이 필요합니다.
- 타임아웃은 신뢰성 잇는 장애 감지기가 아닙니다.
- 이러한 이유로 2PC가 계속 사용됩니다.
#
현실의 분산 트랜잭션분산 트랜잭션은 크게 두 가지 종류로 사용됩니다.
- 데이터베이스 내부 분산 트랜잭션
- 어떤 분산 데이터베이스는 데이터베이스 노드 사이에 내부 트랜잭션을 지원합니다.
- 이종 분산 트랜잭션
- 서로 다른 벤더의 데이터베이스일 수도, 메시지 브로커처럼 비데이터베이스 시스템일 수도 있습니다.
#
정확히 한 번 메시지 처리- 이종 분산 트랜잭션은 다양한 시스템이 강력한 방법으로 봉합될 수 있게 합니다.
- 메시지 전달이나 데이터베이스 트랜잭션 중 하나가 실패하면 둘 다 어보트되고 메시지 브로커는 나중에 메시지를 안전하게 다시 전달할 수 있습니다.
- 즉, 메시지와 그 처리 과정의 부수 효과를 원자적으로 커밋함으로써 메시지가 결과적으로(effectively) 정확히 한 번 처리되도록 보장이 가능합니다.
- 위의 분산 트랜잭션은 트랜잭션의 영향을 받는 모든 시스템이 동일한 원자적 커밋 프로토콜을 사용할 수 있을 때만 가능합니다.
#
XA 트랜잭션- X/Open XA(eXtended Architecture) 는 이종 기술에 걸친 2단계 커밋을 구현하는 표준입니다.
- XA는 PostgreSQL, MySQL, DB2, SQL Server, Oracle 등에서 지원됩니다.
- XA는 네트워크 프로토콜이 아니며, 트랜잭션 코디네이터와 연결되는 인터페이스를 제공하는 API일 뿐입니다.
- XA는 애플리케이션이 네트워크 드라이버나 클라이언트 라이브러리를 사용해 참여자 데이터베이스나 메시징 서비스와 통신한다고 가정합니다.
- 트랜잭션 코디네이터는 XA API를 구현합니다.
- 표준은 이를 명시하지는 않았으나 일반적으로 단순한 라이브러리입니다.
- 애플리케이션 프로세스가 죽거나 애플리케이션이 실행 중인 장비가 죽으면 코디네이터도 함께 사라집니다.
- 코디네이터 로그는 애플리케이션 서버의 로컬 디스크에 있으므로 서버는 재시작돼야하고 코디네이터 라이브러리가 로그를 읽어서 각 트랜잭션의 커밋/어보트 결과를 복구해야 합니다.
#
의심스러운 상태에 잇는 동안 잠금을 유지하는 문제- 정리될 의심스러운 트랜잭션을 무시하지 못하는 이유는 잠금과 관련이 있습니다.
- 데이터베이스는 트랜잭션이 커밋하거나 어보트할 때까지 이런 잠금을 해제할 수 없습니다.
- 그러므로 2단계 커밋을 사용할 때 트랜잭션은 의심스러운 상태에 있는 동안 내내 잠금을 잡고 있어야 합니다.
- 이러한 잠금이 유지되는 동안 다른 어떤 트랜잭션도 그 로우를 변경할 수 없습니다.
- 즉, 다른 트랜잭션이 자기 일을 계속할 수 없습니다.
- 이는 의심스러운 트랜잭션이 해소될 때까지 애플리케이션의 많은 부분을 사용할 수 없게 되는 원인이 됩니다.
#
코디네이터 장애에서 복구하기- 이론상으로는 코디네이터가 죽은 후 재시작하면 로그로부터 그 상태를 깨끗하게 복구하고 의심스러운 트랜잭션을 해소해야 합니다.
- 현실에서는 고아가 된(orphaned) 의심스러운 트랜잭션, 즉 결과를 경정할 수 없는 트랜잭션이 생길 수 있습니다.
- 데이터베이스 서버를 재부팅해도 이 문제를 고칠 수 없습니다.
- 빠져나가는 유일한 방법은 관리자가 수동으로 트랜잭션을 커밋하거나 롤백할지 결정하는 것 뿐입니다.
- 다만 이러한 경우는 심각한 서비스 중단이 발생한 경우일텐데, 스트레스가 높고 시간 압박이 있는 사태에서 처리해야 할 가능성이 높습니다.
- 여러 XA 구현에서는 참여자가 코디네이터로부터 확정적 결정을 얻지 않고 의심스러운 트랜잭션을 어보트하거나 커밋할지 일반적으로 결정할 수 있도록 경험적 결정(heuristic decision) 가 있습니다.
- 여기서 경험적은 2단계 커밋의 약속 체계를 위반하기 때문에 아마도 원자성을 깰 수 있다를 완곡하게 표현한 것입니다.
- 즉, 경험적 결정은 평상시가 아닌 큰 장애 상황을 벗어나고자 할 때만 쓰도록 의도된 것입니다.
#
분산 트랜잭션의 제약- XA 트랜잭션은 여러 참여 데이터 시스템이 서로 일관성을 유지하게 하는 실제적이고 중요한 문제를 해결해 주지만, XA 트랜잭션도 중요한 운영상 문제를 가져옵니다.
- 핵심 구현은 트랜잭션의 코디네이터 자체가 일종의 데이터베이스여야 하고, 따라서 다른 중요한 데이터베이스와 동일하게 신경 써서 접근해야 합니다.
- 코디네이터가 복제되지 않고 단일 장비에서만 실행되면 전체 시스템의 단일 장애점이 됩니다. (그러나 구현은 기본적으로 고가용성을 제공하지 않거나 가장 기초적인 복제만 지원합니다.)
- 여러 서버 사이드 애플리케이션은 모든 영속적인 상태를 데이터베이스에 저장하고 상태 비저장 모드로 개발됩니다. 그러면 애플리케이션 서버를 마음대로 추가하고 제거할 수 있다는 이점이 있습니다. 그러나 코디네이터가 애플리케이션 서버의 일부가 되면 배포의 특성이 바뀌게 됩니다. 즉, 코디네이터의 로그가 지속적인 시스템 상태의 중대한 부분이 되며 애플리케이션 서버는 더 이상 상태 비저장이 아니게 됩니다.
- XA는 광범위한 데이터 시스템과 호환돼야 하므로 최소 공통 분모가 될 필요가 있습니다.
- 분산 트랜잭션은 장애를 증폭시키는 경향이 있으며 이는 내결함성을 지닌 시스템을 구축하려는 목적에 어긋납니다.
일관성을 유지하기는 어렵습니다. 이종 분산 트랜잭션의 고통 없이 같은 결과를 달성하는 대안이 있습니다. 그 전에 합의에 대한 주제를 마무리 지어야 합니다.
#
내결함성을 지닌 합의- 비공식적으로 합의는 여러 노드가 어떤 것에 동의해야 한다는 뜻입니다.
- 합의 문제는 하나 또는 그 이상의 노드들이 값을 제안할 수 있고 합의 알고리즘이 그 값 중 하나를 결정 에 대해 형식화됩니다.
- 합의 알고리즘은 다음 속성을 만족해야 합니다.
- 균일한 동의, 어떤 두 노드도 다르게 결정하지 않습니다.
- 무결성, 어떤 노드도 두번 결정하지 않습니다.
- 유효성, 한 노드가 값 v를 결정한다면 v는 어떤 노드에서 제안된 것입니다.
- 종료, 죽지 않은 모든 노드는 결국 어떤 값을 결정합니다.
- 균일한 동의와 무결성 속성은 합의의 핵심 아이디어를 정의합니다. 즉 모두 같은 결과로 결정하며 한 번 결정하면 마음을 바꿀 수 있습니다.
- 내결함성이 상관없다면 처음 세 개의 속성을 만족시키는 것은 쉽습니다.
- 종료 속성은 내결함성의 아이디어를 형식화합니다. 즉, 합의 알고리즘은 그냥 그 상태에서 머무는 것이 아닌 진행해야 한다고 규정합니다.
- 합의 시스템 모델은 노드가 "죽으면" 노드가 갑자기 사라져서 결코 돌아오지 않는다고 가정합니다.
- 모든 노드가 죽어서 아무 노드도 실행 중이 아니라면 어떤 알고리즘을 쓰든지 아무것도 결정할 수 없습니다.
- 즉, 알고리즘이 견딜 수 있는 장애의 수에는 제한이 있습니다.
- 종료 속성은 죽거나 연결할 수 없는 노드 대수가 절반 미만이라는 가정에 종속적입니다.
- 대부분의 합의 알고리즘은 비잔틴 결함이 없다고 가정합니다. 즉, 노드가 프로토콜을 올바르게 따르지 않으면 프로토콜의 안전성 속성이 깨질수도 있습니다.
#
합의 알고리즘과 전체 순서 브로드캐스트- 재결합성을 지닌 합의 알고리즘 중 대다수는 실제로는 여기서 설명한 형식적 모델(동의, 무결성, 유효성, 종료 속성)을 직접 사용하지 않습니다.
- 대신 값의 순차열(sequence) 에 대해 결정해서 이번 장 앞부분에서 설명한 전체 순서 브로드캐스트 알고리즘을 만니다.
- 전체 순서 브로드캐스트를 하려면 모든 노드에게 메시지가 정확히 한 번, 같은순서로 전달되어야 합니다.
- 전체 순서 브로드캐스트는 합의를 여러 번 반복하는 것과 동일합니다.
- 합의의 동의 속성 때문에 모든 노드는 같은 메시지를 같은 순서로 전달하도록 결정합니다.
- 무결성 속성 때문에 메시지는 중복되지 않습니다.
- 유효성 속성 때문에 메시지는 오염되지 않고 난데없이 조작되지 않습니다.
- 종료 속성 때문에 손실되지 않습니다.
#
단일 리더 복제와 합의- 5장의 단일 리더 복제의 경우, 리더를 어떤식으로 선택하느냐에 있습니다. 리더를 운영팀에 있는 사람이 수동으로 선택해서 설정한다면 본질적으로 독재자 방식의 "합의 알고리즘"을 사용하는 것입니다.
- 어떤 데이터베이스는 기존 리더에 장애가 나면 팔로워 하나를 새 리더로 승격시켜 자동 리더 선출과 장애 복구를 수행합니다.
- 다만, 리더를 선출하려면 리더가 필요한 것 처럼 보입니다. 이 난제를 벗어나기 위해서는 따로 필요합니다.
#
에포크 번호 붙이기와 정족수- 합의 프로토콜은 모두 내부적으로 어떤 형태로든 리더를 사용하지만 리더가 유일하다고 보장하지는 않습니다. 대신 더 약한 보장을 할 수 있습니다.
- 이 프로토콜들은 에포크 번호(epoch number) 를 정의하고 각 에포크 내에서는 리더가 유일하다고 보장합니다.
- 현재 리더가 죽었다고 생각될 때마다 새 노드를 선출하기 위해 노드 사이에서 투표를 해서 에보크 번호가 높은 리더가 이겨서 리더가 됩니다.
- 노드의 정족수로부터 투표를 받아서 리더를 판단합니다. 정족수는 항상은 아니지만 노드의 과반수로 구성됩니다.
- 즉, 한 번은 리더를 선출하기 위해, 두 번째는 리더의 제안에 투표하기 위해서 총 두 번의 투표가 있습니다.
- 2PC와 비슷해 보이지만 2PC로 부터 "네" 투표가 필요하지만 내결함성을 지닌 합의 알고리즘은 노드의 과반수로부터만 투표를 받으면 됩니다.
#
합의의 제약- 합의 알고리즘은 분산 시스템의 커다란 발전이며, 불확실한 시스템에 구체적인 안전성 속성(동의, 무결성, 유효성)을 가져오고, 그럼에도 내결함성을 유지합니다.
- 제안이 결정되기 전에 노드가 제안에 투표하는 과정은 일종의 동기식 복제입니다.
- 합의 시스템은 항상 엄격한 과반수가 동작하기를 요구합니다.
- 대부분의 합의 알고리즘은 투표에 참여하는 노드 집합이 고정되어 있다고 가정하며 클러스터에 노드를 그냥 추가하거나 제거할 수 없습니다.
- 합의 알고리즘의 동적 멤버십(dynamic membership) 확장은 클러스터에 있는 노드 집합이 시간이 지남에 따라 바뀌는 것을 허용합하지만 이들은 정적 멤버십 알고리즘보다 훨씬 더 이해하기 어렵습니다.
- 합의 시스템은 장애 노드를 감지하기 위해 일반적으로 타임아웃에 의존합니다.
- 일시적인 네트워크 문제 때문에 노드가 리더에 장애가 발생햇다고 잘못 생각하는 일이 종종 발생합니다.
- 합의 알고리즘은 네트워크 문제에 특히 민감합니다.
- 네트워크가 불안하다면 리더가 끊임없이 바뀌면서 시스템이 진행을 못하는 경우가 발생할 수 있습니다.
#
멤버십과 코디네이션 서비스- 주키퍼나 etcd 같은 프로젝트는 종종 "분산 키-값 저장소"나 "코디네이션과 설정 서비스"라고 설명됩니다.
- 애플리케이션 개발자는 주키퍼를 직접 쓸 필욘는 거의 없습니다.
- 실제로 범용 데이터베이스에는 실제 범용 데이터베이스에는 적합하지 않습니다.
- HBase, 하둡, 노바, 카프카는 모두 주키퍼에 의존합니다.
- 주키퍼와 etcd는 완전히 메모리 안에 들어올 수 있는 작은 양의 데이터를 보관하도록 설계합니다.
- 소량의 데이터는 내결함성을 지닌 전체 순서 브로드캐스트 알고리즘을 사용해 모든 노드에 걸쳐 복제됩니다.
- 주키퍼는 다른 흥미로운 기능 집합도 구현합니다.
- 선형적 원자적 연산, 원자적 compare-and-set 연산을 사용해 잠금을 구현할 수 있습니다.
- 연산의 전체 순서화, 펜싱 토큰 등을 사용해 클라이언트 충돌을 막습니다.
- 장애 감지, 세션에서 획득한 잠금은 세션이 타임아웃 됐을 때 자동으로 해제되도록 설정할 수 있습니다.
- 변경 알림, 클라이언트는 다른 클라이언트가 생성한 잠금과 값을 읽을 수 있을 뿐만 아니라 거기에 변경이 있는지 감시할 수도 있습니다.
#
작업을 노드에 할당하기- 주키퍼/처비 모델이 잘 동작하는 한 가지 예는 여러 개의 프로세스나 서비스가 있고 그중 하나가 리더나 주 구성요소로 선택돼야 할 때입니다.
- 파티셔닝된 자원(데이터베이스, 메시지 스트림, 파일 저장소, 분산 액터)이 있고 어떤 파티션을 어느 노드에 할당해야 할지 결정해야 하는 경우입니다.
- 새 노드들이 클러스터에 합류하면서 부하의 재균형화를 위해 어떤 파티션들은 기존 노드에서 새로운 노드로 이동돼야 합니다.
- 애플리케이션이 단일 노드가 아닌 수많은 노드에서 과반수 투표를 하는 것은 매우 비효율적이므로, 주키퍼는 고정된 수의 노드에서 실행되고 이 노드들 사이에서 과반수 투표를 수행하면서 많아질 수 있는 클라이언트를 지원합니다.
- 즉, 주키퍼는 노드들을 코디네이트하는 작업의 일부를 외부 서비스에 위탁하는 방법을 제공합니다.
#
서비스 찾기- 주키퍼, etcd, Consul은 서비스 찾기(service discovery) , 즉 특정 서비스에 연결하려면 어떤 IP 주소로 접속해야 하는지 알아내는 용도로도 자주 사용됩니다.
- DNS는 서비스 이름으로 IP 주소를 찾는 전통적인 방법이고 좋은 성능과 가용성을 달성하기 위해 다층 캐시를 사용합니다.
- DNS는 신뢰성 있게 사용 가능하고 네트워크 끊김에 견고합니다.
- 서비스 찾기는 합의가 필요 없지만 리더 선출은 합의가 필요합니다.
- 즉, 합의 시스템이 누가 리더인지 이미 안다면 다른 서비스들이 리더가 누구인지 찾는데 그 정보를 사용하는 것도 타당합니다.
#
멤버십 서비스- 주키퍼와 유사 프로젝트들은 오랜 멤버십 서비스(membership service) d연구 역사의 일부로 볼 수 있습니다.
- 멤버십 서비스는 클러스터에서 어떤 노드가 현재 활성화된 살아 있는 멤버인지 결정합니다.
- 조드가 실제로는 살아 있지만 합의에 의해 죽은 것으로 잘못 선언될 가능성이 있습니다.
#
정리- 선형성은 이해하기 쉬우나, 느리다는 단점이 있습니다.
- 시스템에서 발생하는 이벤트에 순서를 부과하는 인과성은 더 약한 일관성 모델을 제공하며, 선형성의 코디네이션 오버헤드가 없고 네트워크 문제에 훨씬 덜 민감합니다.
- 인과적 순서로 해결이 안되는 경우, 합의를 사용합니다.
- 합의를 달성하는 것은 결정된 것에 모든 노드가 동의하고 결정을 되돌릴 수 없는 방식으로 뭔가를 결정한다 것입니다.
- 선형성 compare-and-set 레지스터, 레지스터는 현재 값이 연산의 매개변수로 넘겨진 값과 같은지 여부에 따라 값을 설정할지 말지 원자적으로 결정합니다.
- 원자적 트랜잭션 커밋, 데이터베이스는 분산 트랜잭션을 커밋할 것인지 어보트할 것인지 결정해야 합니다.
- 전체 순서 브로드캐스트, 메시징 시스템은 메시지를 전달할 순서를 결정해야 합니다.
- 잠금과 임차원, 여러 클라이언트들이 잠금이나 임차권을 얻기 위해 경쟁하고 있을 때 잠금은 누가 성공적으로 잠금을 획득할지 결정합니다.
- 멤버십/코디네이션 서비스, 장애 감지기가 주어지면 시스템은 어떤 노드가 죽은지, 살은지를 결정해야합니다.
- 유일성 제약 조건, 여러 트랜잭션들이 동시에 같은 키로 충돌하는 레코드를 생성할 때 어떤 것을 허용하고 실패하도록 결정해야 합니다.
- 이 모든 것들은 노드가 하나만 있거나 결정하는 능력을 한 노드에만 준다고 하면 간단합니다.
- 다만 단일 리더가 장애가 나거나 네트워크가 끊겨서 리더에 접속할 수 없게 되면 이런 시스템은 아무 진행도 하지 못하게 되며, 이를 처리하는 방법은 세가지 방법이 있습니다.
- 리더가 복구될 때까지 기다리고 시스템이 그동안 차단되는 것을 받아들입니다.
- 사람이 새 리더 노드를 선택하고 시스템이 그 노드를 사용핟도록 재설정해서 수동으로 장애 복구를 합니다.
- 자동으로 새 리더를 선택하는 알고리즘을 사용합니다.
- 단일 리더 데이터베이스는 모든 쓰기마다 합의 알고리즘을 실행하지는 않고 선형성을 제공할 수는 있지만, 리더십 변경을 할때는 합의가 필요합니다.
- 주키퍼 같은 도구는 애플리케이션이사용할 수 있는 합의, 장애 감지, 멤버십 서비스를 위탁하는데 중요한 역할을 합니다.
- 내결함성을 지니기를 원한다면 주키퍼 같은 것을 쓰는 것이 현명합니다.
- 모든 시스템이 반드시 합의가 필요한 것은 아닙니다. (ex. 리더가 없는 복제 시스템)