← 문제로

24. 쿼럼 미충족(R+W≤N)과 버전 무시로 인한 stale 읽기

난이도 최상
내 리뷰 · C#
해설 · C#

해설 — 쿼럼 미충족(R+W≤N)과 버전 무시로 인한 stale 읽기

난이도: 최상

요약

W 개 ack 로 쓰기를 성공 처리하고 (A), R 개 응답 중 첫 응답을 그대로 반환 (B) 한다. 게다가 N=3, W=1, R=1 이면 읽기 쿼럼과 쓰기 쿼럼이 겹치지 않을 수 있어(R+W≤N), 방금 쓴 값을 못 본 복제본을 읽어 예전 값이 나온다. 버전 비교도 없어 최신 값을 골라내지도 못한다.

문제점

  • [쿼럼 교집합 부재] R+W ≤ N: 강한 읽기 일관성은 읽기 쿼럼과 쓰기 쿼럼이 최소 한 복제본에서 겹쳐야 성립한다 → 조건 R + W > N. N=3, W=1, R=1 이면 1+1 ≤ 3 이라 교집합이 보장되지 않는다. 쓰기가 복제본 A 에만 반영되고, 읽기가 B 를 고르면 옛 값을 본다.
    • 재현: 인벤토리에 아이템 추가(version=5)가 A 에만 ack 되어 성공 반환 → 곧이어 읽기가 B(version=4)를 선택 → 방금 넣은 아이템이 사라진 것처럼 보인다(원복). 다음 사용자 행동이 stale 상태 위에서 진행돼 손실·이중 지급으로 번진다.
  • [버전 무시] 첫 응답 반환: (B) 는 responses[0] 을 반환한다. 여러 응답을 받아도 버전이 가장 높은 값을 고르지 않으므로, 우연히 오래된 복제본이 먼저 응답하면 그 값이 나간다.
  • [수렴 부재] 읽기 수리(read-repair) 없음: stale 복제본을 최신으로 되돌리는 수리가 없어 불일치가 오래 지속된다.
  • [write 방식] 순차 대기: (A) 는 태스크를 순서대로 await 하며 첫 W 개를 세지만, 이는 "가장 먼저 끝난 W 개" 가 아니라 "리스트 앞쪽 W 개" 라 느린 복제본에 걸리면 지연이 커진다(부차적).
  • 재현 조건: 복제 지연이 있는 상시 상황. W/R 을 낮춰 지연·가용성을 얻으려 할 때 흔히 실수한다.

수정안

R + W > N 을 만족시키고(예: N=3 이면 W=2, R=2), 읽기는 R 응답 중 최고 버전을 골라 반환하며, 뒤처진 복제본에 최신 값을 되써서 수렴시킨다(read-repair).

public QuorumStore(IReplica[] replicas)
{
    _replicas = replicas;
    _w = replicas.Length / 2 + 1;      // 과반
    _r = replicas.Length / 2 + 1;      // R + W = 2*(⌊N/2⌋+1) > N 보장
}

public async Task<Versioned> ReadAsync(string key)
{
    var tasks = _replicas.Select(rep => rep.GetAsync(key)).ToList();
    var responses = new List<Versioned>();
    while (responses.Count < _r && tasks.Count > 0)
    {
        var done = await Task.WhenAny(tasks);   // 가장 먼저 온 응답부터
        tasks.Remove(done);
        responses.Add(await done);
    }
    var newest = responses.OrderByDescending(v => v.Version).First();  // 최고 버전 선택
    // read-repair: 뒤처진 복제본에 최신 값 되쓰기(비동기 best-effort)
    _ = RepairAsync(key, newest);
    return newest;
}

더 나은 설계

  • 일관성 요구에 따라 W/R 선택: 강한 읽기는 R+W>N. 쓰기 지연을 줄이려면 W 를 낮추고 R 을 높이거나, 반대로 읽기 지연을 줄이려면 R=1, W=N. 트레이드오프를 명시적으로 고른다.
  • 동시 쓰기 충돌 처리: 단조 버전만으로는 동시 쓰기(같은 버전)를 구분 못 한다. 버전 벡터/타임스탬프+ 노드ID 타이브레이커(LWW) 또는 CRDT/충돌 병합으로 다룬다.
  • 읽기 수리 + 안티엔트로피: read-repair 로 핫 키를, 백그라운드 Merkle 트리 동기화로 콜드 키를 수렴시킨다.
  • 선형화가 꼭 필요하면: 쿼럼 저장소 대신 합의(Raft/Paxos) 기반 저장소나 리더 기반 강한 일관성을 쓴다 (인벤토리/재화처럼 손실이 치명적인 데이터).
  • 트레이드오프: R+W>N 은 일관성을 주지만 가용성(한 복제본 장애 시 쿼럼 미달)과 지연이 나빠진다 — CAP/PACELC 상의 선택.

면접 포인트

  • 쿼럼 일관성 조건 R+W>N 의 의미(읽기·쓰기 쿼럼의 교집합 보장)와 왜 버전 최댓값 선택이 필요한가.
  • read-repair·안티엔트로피로 수렴시키는 방법, 동시 쓰기의 LWW/버전 벡터 처리.
  • 최종 일관성 쿼럼 vs 합의 기반 강한 일관성의 트레이드오프(가용성·지연·데이터 민감도).