사전 느슨한 정족수
패턴

느슨한 정족수

gabury1

복제본을 여럿 둔 저장소는 정해진 수만큼 응답을 받아야 읽기나 쓰기를 끝난 것으로 칩니다. 느슨한 정족수는 그 응답을 낼 노드를 원래 담당 노드로 한정하지 않기로 한 결정입니다. 살아 있는 다른 노드가 그 자리를 대신 채웁니다. 채워야 하는 수는 그대로 두고 구성원만 푸는 셈입니다.

상세

느슨한 정족수(sloppy quorum)는 정족수를 채울 노드의 자격을 푸는 결정입니다. 엄격한 정족수는 그 키를 맡은 담당 노드 집합 안에서 응답을 받아야 성립합니다. 느슨한 정족수는 그 멤버십을 강제하지 않습니다.

Dynamo 논문은 이 결정을 세우면서 반대편부터 적어 둡니다. 전통적인 정족수 방식을 썼다면 서버 장애와 네트워크 분단 동안 시스템을 쓸 수 없었을 것이고, 가장 단순한 장애 조건에서도 내구성이 떨어졌을 것이라고 적습니다. 그것을 고치려고 엄격한 정족수 멤버십을 강제하지 않고 대신 느슨한 정족수를 쓴다는 것이 논문의 문장입니다.

푸는 방식은 한 문장에 들어 있습니다. 모든 읽기와 쓰기 연산은 선호 목록에서 살아 있는 첫 N 개 노드에 수행됩니다. 논문은 그 N 개가 일관성 해싱 링을 걸으며 처음 만나는 첫 N 개 노드와 언제나 같지는 않다고 적습니다. 살아 있는 노드로 목록을 훑어 내려가므로 담당 자리에서 밀려난 노드가 그 안에 들어옵니다.

바뀌는 것은 수가 아닙니다. 쓰기 하나가 성공하려면 몇 개가 응답해야 하는지는 그대로입니다. 바뀌는 것은 그 응답을 누가 내도 되느냐입니다. 논문은 N 과 함께 읽기 쪽 설정값 R 과 쓰기 쪽 설정값 W 를 두고 그 구성을 별도의 절에서 다룬다고 적습니다.

대가

정족수가 값을 지키는 방식은 겹침입니다. Cassandra 공식 문서는 복제본 집합이 서로 겹치도록 읽기와 쓰기 일관성 수준을 고르는 것이 흔하다고 적습니다. 그렇게 하면 확인된 모든 쓰기가 뒤이은 읽기에 보입니다. 문서는 이것을 Dynamo 와 같은 말로 W + R > RF 라고 적습니다. W 는 쓰기 일관성 수준입니다. R 은 읽기 일관성 수준입니다. RF(Replication Factor)는 복제 계수입니다.

느슨한 정족수는 그 겹침의 전제를 흔듭니다. 응답을 낸 노드가 담당 노드가 아닐 수 있기 때문입니다. Riak 공식 문서는 느슨한 정족수를 무시하도록 요청을 설정할 수 있다고 적고, 그 목적을 오래된 데이터가 클라이언트에게 돌아갈 가능성을 제한하는 것이라고 적습니다. 뒤집어 읽으면 느슨한 정족수를 허용하는 동안에는 오래된 데이터가 돌아갈 수 있다는 말입니다.

같은 문서가 든 장면이 그것을 그대로 보여 줍니다. 노드 하나가 죽으면 Riak 의 느슨한 정족수 기능이 발동해 다른 노드가 그 요청을 대신 맡습니다. 장애 뒤 처음 던지는 읽기에서 R 이 1 이면 Riak 에게서 not found 응답을 받을 위험이 큽니다. 책임을 넘겨받은 노드는 아직 그 키의 사본이 없고, 없는 키를 확인하는 편이 디스크에서 값의 사본을 꺼내 오는 것보다 훨씬 빠르기 때문에 그 노드가 가장 먼저 응답할 공산이 큽니다.

쓰기 쪽 설정값을 낮추면 같은 자리가 넓어집니다. Dynamo 논문은 가장 높은 수준의 가용성이 필요한 애플리케이션이 W 를 1 로 둘 수 있다고 적습니다. 그러면 시스템 안의 노드 하나가 그 키를 자기 로컬 저장소에 내구성 있게 쓴 것만으로 쓰기가 받아들여집니다. 쓰기 요청은 시스템의 모든 노드가 사용 불가능할 때만 거절됩니다. 다만 논문은 실제로는 프로덕션의 대부분의 Amazon 서비스가 원하는 내구성 수준을 맞추려고 더 높은 W 를 쓴다고 적습니다.

값이 갈리는 것은 나중에 치릅니다. Dynamo 논문의 초록은 이 수준의 가용성을 얻기 위해 특정 장애 시나리오에서 일관성을 희생한다고 적습니다. 그리고 객체 버전 관리와 애플리케이션이 돕는 충돌 해결을 광범위하게 쓴다고 적습니다. 갈린 버전을 누가 어떻게 합칠지가 이 결정에 따라오는 일입니다.

대신 받아 둔 값이 끝내 제자리로 못 가는 경우도 있습니다. Cassandra 공식 문서는 힌트가 최선 노력이며 anti-entropy 복구처럼 최종 일관성을 보장하지는 않는다고 적습니다. 노드가 제때 돌아오지 않으면 대상 복제본은 읽기 복구나 전체 또는 증분 anti-entropy 복구가 그 변경을 전파할 때까지 영구히 어긋난 상태로 남습니다.

동작

담당 노드가 빠진 자리를 다른 노드가 채우는 절차가 힌티드 핸드오프입니다. Dynamo 논문이 4.6절에서 든 예는 N 을 3 으로 둔 구성이고, 노드 A 와 노드 D 가 나옵니다.

sequenceDiagram
    participant D as 노드 D
    participant A as 노드 A
    Note over A: 쓰기 도중 일시적으로 다운
    Note over D: A 에 살았을 복제본이 D 로 보내진다
    Note over D: 복제본 메타데이터에 원래 수신자가 A 라는 힌트가 붙어 있다
    D->>D: 별도 로컬 데이터베이스를 주기적으로 훑는다
    Note over A: 회복
    D->>A: 복제본을 전달하려 시도한다
    Note over D: 전달에 성공하면 자기 사본을 지워도 된다

단계는 여섯입니다.

  1. 쓰기 도중 노드 A 가 일시적으로 다운되거나 도달 불가능합니다.
  2. 원래 A 에 살았을 복제본이 노드 D 로 보내집니다. 논문은 이것이 원하는 가용성과 내구성 보장을 지키기 위한 것이라고 적습니다.
  3. D 로 간 복제본은 메타데이터에 힌트를 달고 갑니다. 그 힌트는 이 복제본을 원래 받기로 되어 있던 노드가 A 라는 것을 가리킵니다.
  4. 힌트가 붙은 복제본을 받은 노드는 그것을 별도의 로컬 데이터베이스에 보관하고 주기적으로 훑습니다.
  5. A 가 회복된 것을 감지하면 D 는 복제본을 A 에게 전달하려 시도합니다.
  6. 전달이 성공하면 D 는 시스템 전체의 복제본 수를 줄이지 않고 자기 로컬 저장소에서 그 객체를 지워도 됩니다.

정족수 쪽에서 보면 2번이 갈림입니다. 쓰기 하나가 응답을 하나 확보했고, 그 응답을 낸 것이 담당 노드가 아닙니다. 담당 노드가 그 값을 갖는 시점은 전달이 성공하는 5번입니다. 2번부터 5번까지가 담당 노드 집합과 실제 응답 집합이 어긋나 있는 구간입니다. 6번은 D 가 자기 사본을 지워도 되는 선택이고, 담당 노드가 그 값을 갖는 것과는 상관이 없습니다. 논문은 이 방식으로 일시적인 노드 장애나 네트워크 장애 때문에 읽기와 쓰기가 실패하지 않도록 한다고 적습니다.

경계

힌티드 핸드오프를 켜 두면 그것만으로 느슨한 정족수인가. 아닙니다. 가르는 것은 힌트가 정족수 계산에 들어가느냐입니다.

Cassandra 공식 문서가 적은 일관성 수준 목록이 그 선을 보여 줍니다. ONE 은 복제본 하나가 응답해야 합니다. QUORUM 은 복제본의 과반이 응답해야 합니다. ALL 은 복제본 전부가 응답해야 합니다. 이 수준들은 모두 실제 응답을 셉니다. 예외가 하나 있습니다. ANY 는 복제본 하나가 응답하거나 코디네이터가 힌트를 저장해도 된다고 적혀 있습니다. 힌트가 저장되면 코디네이터가 나중에 그 힌트를 재생해 변경을 복제본에 전달하려 시도합니다. 이 수준은 쓰기 연산에서만 받아들여집니다.

같은 프로젝트의 힌트 문서가 든 타임라인이 판정을 굳혀 줍니다. 복제 계수가 3 인 키스페이스에 LOCAL_QUORUM 으로 변경을 넣는 예입니다. 코디네이터가 세 복제본에 변경을 보내고 셋 중 둘이 확인하면 클라이언트에게 성공을 돌려줍니다. 복제본 하나가 사용 불가능하면 코디네이터는 힌트를 자기 파일시스템에 저장합니다. 문서의 타임라인에서 클라이언트가 정족수 확인을 받는 시점은 힌트가 저장되기 전입니다. 힌트 저장은 기본값 2초인 쓰기 타임아웃이 지난 뒤에 일어납니다. 정족수를 채운 것은 실제로 응답한 두 복제본입니다.

그래서 판정은 이렇습니다. Cassandra 에서 힌티드 핸드오프가 켜져 있어도 ANY 를 고르지 않는 한 정족수는 실제 응답으로만 채워집니다. 힌트를 쌓는 것과 정족수의 구성원을 푸는 것은 다른 일입니다.

예시

Apache Cassandra 의 ANY 일관성 수준

ANY   복제본 하나가 응답하거나 코디네이터가 힌트를 저장하면 성립한다

Cassandra 에서 정족수의 구성원이 실제로 풀리는 자리가 이 한 줄입니다. 쓰기 연산에서만 받아들여 집니다. 이 수준으로 받은 성공은 복제본 하나가 낸 것일 수도 있고, 아직 어느 복제본도 받지 못한 채 코디네이터에 쌓인 힌트일 수도 있습니다. 응답을 낸 쪽이 담당 복제본이 아닌 자리가 여기입니다.

이 수준이 쓸모 있으려면 힌트 자체가 켜져 있어야 합니다. 공식 문서는 힌트가 데이터 일관성에 중요하기 때문에 기본으로 켜져 있다고 적습니다. cassandra.yaml 에서 그것을 켜고 끄는 값은 하나입니다.

hinted_handoff_enabled: true                # 힌티드 핸드오프를 켜고 끈다. 기본값 true

Riak KV 의 PR 과 PW

Riak 에는 반대 방향의 손잡이가 있습니다. 느슨한 정족수를 끄는 설정입니다.

PR = 2   살아남은 기본 노드가 둘이므로 요청이 성공한다
PR = 3   세 번째 기본 노드가 오프라인이라 요청이 실패한다

PR(primary read)은 응답을 낼 노드를 기본 노드로 한정하는 값입니다. 공식 문서가 든 장면은 기본 노드 셋 중 하나가 오프라인이 된 상황입니다. 대체 노드가 그 키의 책임을 넘겨받아 요청을 받으면 그런 키가 없다고 답하지만, 살아남은 두 기본 노드는 값을 알고 있습니다. PR 을 지정하면 살아남은 기본 노드만 그 데이터의 유효한 출처로 칩니다. 쓰기 쪽 짝은 PW(primary write)이고 같은 문서가 둘을 함께 다룹니다.

대가도 문서가 그대로 적어 둡니다. 대체 노드가 요청을 처리하지 못하게 하면 요청이 실패할 위험이 늘어납니다. 느슨한 정족수를 끄는 값이라 끄는 만큼 가용성을 내주는 자리입니다.

관련 항목

이것이 속하는 상위 분류

정족수

이것과 맞세워지는 대립 개념

엄격한 정족수 · 정족수 교집합 · 최종 일관성 · 일관성

이것을 이루는 절차와 그 절차가 쓰는 값

힌티드 핸드오프 · 선호 목록(preference list) · 일관성 해싱

이것이 동작할 때 관여하는 역할·참여자

노드 · 코디네이터 · 클라이언트 · 복제

이것을 실제로 채택한 저장소

Dynamo · Cassandra · Riak KV

이것이 치르는 대가를 다루는 절차

반-엔트로피 · 읽기 복구 · 버전관리 · 충돌 해소

다른 이름: sloppy quorum · 슬로피 쿼럼