8. 분산 전역 한도 카운터의 초과 판매(local count + 지연 동기화) (C#)
난이도 최상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ─────────────────────────────────────────────────────────────
// 시나리오: 글로벌 한정 판매(예: 전 서버 통합 한정 아이템 1000개, 또는
// 전역 구매/동시접속 한도)를 여러 게임 서버 노드가 공유한다. 각 노드는
// 로컬 카운터로 자기 판매 수를 세고, 주기적으로 중앙 저장소(Redis 류)에
// 자기 기여분을 더하고 전역 합계 스냅샷을 받아온다. 판매 요청은 각 노드에서
// 초당 수천 건 들어오므로, 매 건마다 중앙을 왕복하지 않으려고 로컬 스냅샷으로
// 빠르게 판정한다.
//
// 요구사항:
// - 전역 판매 수가 한도(_limit)를 절대 넘기면 안 된다.
// - 정확성을 지키되, 매 판매마다 중앙 왕복을 강제하지 않는 설계가 바람직하다.
//
// 과제: (A)(B) 지점을 검토하라. 노드가 N개일 때 동기화 주기 사이에
// 전역 합계가 한도를 얼마나 초과할 수 있는지 설명하고,
// 정확성과 처리량을 모두 만족하는 수정안을 제시하라.
// ─────────────────────────────────────────────────────────────
using System.Threading;
using System.Threading.Tasks;
namespace Distributed
{
public interface IKvStore
{
Task<long> IncrByAsync(string key, long delta);
Task<long> GetAsync(string key);
}
public sealed class GlobalSaleLimiter
{
private readonly IKvStore _store;
private readonly long _limit;
private const string Key = "sold_total";
private long _localSold; // 이 노드가 판 수(아직 중앙 미반영)
private long _globalSnapshot; // 마지막으로 본 전역 합계
public GlobalSaleLimiter(IKvStore store, long limit)
{
_store = store;
_limit = limit;
}
// 주기 타이머가 호출: 내 판매분을 전역에 더하고 최신 합계를 받아온다
public async Task SyncAsync()
{
long contrib = Interlocked.Exchange(ref _localSold, 0);
await _store.IncrByAsync(Key, contrib);
_globalSnapshot = await _store.GetAsync(Key);
}
// 한 건 판매 시도(핫패스: 중앙 왕복 없이 로컬 스냅샷으로 판정)
public bool TrySell()
{
// (A)
if (_globalSnapshot + _localSold < _limit)
{
// (B)
_localSold++;
return true;
}
return false;
}
}
} 결함 코드 · C++
// ─────────────────────────────────────────────────────────────
// 시나리오: 글로벌 한정 판매(전 서버 통합 한정 아이템 N개, 또는 전역
// 구매/동시접속 한도)를 여러 게임 서버 노드가 공유한다. 각 노드는 로컬
// 카운터로 자기 판매 수를 세고, 주기적으로 중앙 저장소(Redis 류)에 자기
// 기여분을 더하고 전역 합계 스냅샷을 받아온다. 판매 요청이 폭주하므로
// 매 건마다 중앙을 왕복하지 않으려고 로컬 스냅샷으로 빠르게 판정한다.
//
// 요구사항:
// - 전역 판매 수가 한도(limit_)를 절대 넘기면 안 된다.
// - 정확성을 지키되 매 판매마다 중앙 왕복을 강제하지 않는 설계가 바람직하다.
//
// 과제: (A)(B) 지점을 검토하라. 노드가 N개일 때 동기화 주기 사이에 전역
// 합계가 한도를 얼마나 초과할 수 있는지 설명하고, 정확성과 처리량을
// 모두 만족하는 수정안을 제시하라.
// ─────────────────────────────────────────────────────────────
#include <cstdint>
#include <string>
namespace distributed {
// 중앙 저장소 클라이언트(드라이버 구현 — 여기선 선언만)
struct KvStore {
int64_t incrBy(const std::string& key, int64_t delta); // 원자 증가 후 결과 반환
int64_t get(const std::string& key);
};
class GlobalSaleLimiter {
public:
GlobalSaleLimiter(KvStore& store, int64_t limit)
: store_(store), limit_(limit) {}
// 주기 타이머가 호출: 내 판매분을 전역에 더하고 최신 합계를 받아온다
void sync() {
int64_t contrib = localSold_;
localSold_ = 0;
store_.incrBy(key_, contrib);
globalSnapshot_ = store_.get(key_);
}
// 한 건 판매 시도(핫패스: 중앙 왕복 없이 로컬 스냅샷으로 판정)
bool trySell() {
// (A)
if (globalSnapshot_ + localSold_ < limit_) {
// (B)
++localSold_;
return true;
}
return false;
}
private:
KvStore& store_;
int64_t limit_;
int64_t localSold_ = 0; // 이 노드가 판 수(아직 중앙 미반영)
int64_t globalSnapshot_ = 0; // 마지막으로 본 전역 합계
std::string key_ = "sold_total";
};
} // namespace distributed 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.