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

난이도 최상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] 복제 저장소의 쿼럼 설정과 읽기 일관성 (서버-서버)
// ----------------------------------------------------------------------------
// 시나리오:
//   플레이어 인벤토리를 N=3 복제본을 가진 최종 일관성 키-값 저장소에 보관한다.
//   - 쓰기: 모든 복제본에 보내고 W 개의 ack 를 받으면 성공으로 간주한다.
//   - 읽기: 모든 복제본에 읽기를 보내고 R 개의 응답이 오면 그중 하나를 반환한다.
//   각 값에는 단조 증가 버전이 붙는다.
//
// 요구사항:
//   - 방금 쓴 값이 곧이어 읽을 때 예전 값으로 되돌아가면 안 된다(강한 읽기 일관성).
//
// 과제:
//   QuorumStore 의 (A) 쓰기 성공 기준과 (B) 읽기 응답 선택을 검토하라.
//   N=3, W=1, R=1 설정에서 쓰기 직후 읽기가 어떻게 될 수 있는지, 어떻게 고쳐야 하는지 설명하라.
// ============================================================================

using System.Collections.Generic;
using System.Threading.Tasks;

public readonly struct Versioned
{
    public readonly byte[] Value;
    public readonly long Version;
    public Versioned(byte[] v, long ver) { Value = v; Version = ver; }
}

public sealed class QuorumStore
{
    private readonly IReplica[] _replicas;   // 길이 N
    private readonly int _w;
    private readonly int _r;

    public QuorumStore(IReplica[] replicas, int w, int r) { _replicas = replicas; _w = w; _r = r; }

    public async Task WriteAsync(string key, byte[] value, long version)
    {
        int acks = 0;
        var tasks = new List<Task>();
        foreach (var rep in _replicas)
            tasks.Add(rep.PutAsync(key, new Versioned(value, version)));

        foreach (var t in tasks)
        {
            await t;
            if (++acks >= _w) return;          // (A) W 개 ack 이면 성공 반환
        }
    }

    public async Task<Versioned> ReadAsync(string key)
    {
        var responses = new List<Versioned>();
        var tasks = new List<Task<Versioned>>();
        foreach (var rep in _replicas)
            tasks.Add(rep.GetAsync(key));

        foreach (var t in tasks)
        {
            responses.Add(await t);
            if (responses.Count >= _r) break;  // R 개 모이면 중단
        }
        return responses[0];                   // (B) 첫 응답 반환
    }
}

public interface IReplica
{
    Task PutAsync(string key, Versioned v);
    Task<Versioned> GetAsync(string key);
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.