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++
// ============================================================================
// [코드리뷰 문제] 복제 저장소의 쿼럼 설정과 읽기 일관성 (서버-서버)
// ----------------------------------------------------------------------------
// 시나리오:
// 플레이어 인벤토리를 N=3 복제본을 가진 최종 일관성 키-값 저장소에 보관한다.
// - 쓰기: 모든 복제본에 보내고 W 개 ack 를 받으면 성공.
// - 읽기: 모든 복제본에 읽기를 보내고 먼저 온 R 개 응답 중 "버전이 가장 높은" 값을 반환한다.
// 각 값에는 단조 증가 버전이 붙는다.
//
// 요구사항:
// - 방금 쓴 값이 곧이어 읽을 때 예전 값으로 되돌아가면 안 된다(강한 읽기 일관성).
//
// 과제:
// QuorumStore 의 (A) 쓰기 성공 기준과 (B) 읽기 응답 집합에서의 선택을 검토하라.
// N=3, W=1, R=1 (또는 W=1, R=2) 에서, 읽기가 버전 최댓값을 골라도 왜 예전 값이 나올 수 있는지,
// 어떻게 고쳐야 하는지 설명하라.
// ============================================================================
#include <vector>
#include <string>
#include <cstdint>
#include <algorithm>
struct Versioned {
std::vector<uint8_t> value;
int64_t version = -1;
};
struct IReplica {
virtual void put(const std::string& key, const Versioned& v) = 0;
virtual Versioned get(const std::string& key) = 0;
virtual ~IReplica() = default;
};
class QuorumStore {
public:
QuorumStore(std::vector<IReplica*> replicas, int w, int r)
: replicas_(std::move(replicas)), w_(w), r_(r) {}
void write(const std::string& key, const std::vector<uint8_t>& value, int64_t version) {
int acks = 0;
for (auto* rep : replicas_) {
rep->put(key, Versioned{value, version});
if (++acks >= w_) return; // (A) W 개 ack 이면 성공
}
}
Versioned read(const std::string& key) {
std::vector<Versioned> responses;
for (auto* rep : replicas_) {
responses.push_back(rep->get(key));
if ((int)responses.size() >= r_) break; // R 개 모이면 중단
}
// (B) R 응답 중 버전 최댓값 선택
return *std::max_element(responses.begin(), responses.end(),
[](const Versioned& a, const Versioned& b) {
return a.version < b.version;
});
}
private:
std::vector<IReplica*> replicas_;
int w_;
int r_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.