18. 크로스-샤드 비원자 읽기와 이체의 찢긴 스냅샷 (C#)

난이도 최상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 크로스-샤드 집계 읽기 (재화 총량 조회 / 안티치트 감사)
//
// 플레이어 자산은 샤드로 분산 저장된다: 지갑 잔액은 walletShard, 창고/거래중 물품
// 가치는 vaultShard 에 있다. 운영/안티치트 도구는 "플레이어 총 자산"을 두 샤드에서
// 각각 읽어 합산해 보고한다. 한편 거래 처리기는 지갑에서 차감해 창고로 옮기는
// 이체를 수행한다(두 샤드에 걸친 이동).
//
// 요구사항:
//  - 총 자산 = walletShard 잔액 + vaultShard 가치. 이 합은 이체가 진행 중이어도
//    "돈이 사라지거나 두 번 계산되지 않게" 일관되게 읽혀야 한다(불변식: 총액 보존).
//  - 이체와 합산 조회는 서로 다른 노드에서 동시에 일어난다.
//
// 아래 코드의 (A)(B) 지점을 검토하라.

using System.Threading.Tasks;

public interface IShard
{
    long ReadBalance(string playerId);
    void Apply(string playerId, long delta);   // 잔액에 delta 를 더함(음수 가능)
}

public sealed class AssetService
{
    private readonly IShard _wallet;
    private readonly IShard _vault;
    public AssetService(IShard wallet, IShard vault) { _wallet = wallet; _vault = vault; }

    // 감사 도구가 호출: 플레이어 총 자산 합산.
    public long GetTotalAssets(string playerId)
    {
        long w = _wallet.ReadBalance(playerId);   // (A)
        long v = _vault.ReadBalance(playerId);
        return w + v;
    }

    // 거래 처리기가 호출: 지갑 -> 창고로 amount 이동.
    public void TransferWalletToVault(string playerId, long amount)
    {
        _wallet.Apply(playerId, -amount);         // (B)
        _vault.Apply(playerId, +amount);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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