9. 스캐터-개더 부분 응답을 완전 응답으로 오인 + 무경계 팬아웃·집계 경합 (C#)

난이도 상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 글로벌 경제 감시 서비스가 모든 샤드(존) 서버에 질의해 "전 서버 재화 총량"을
// 합산한다. 이 값은 인플레이션 감시·재화 환수/지급 정책 판단 같은 권위 있는 결정에 쓰인다.
// 코디네이터는 각 샤드에 부분 합계를 요청하고(팬아웃) 응답을 모아 더한다(개더).
//
// 요구사항:
//   - 결과는 "전 샤드를 반영한 값"이어야 하며, 일부 샤드가 응답하지 않으면
//     그 사실이 드러나야 한다(불완전한 값을 완전한 값처럼 쓰면 안 됨).
//   - 샤드 하나가 느리거나 죽어도 코디네이터가 무한정 매달리면 안 된다.
//
// 과제: (A)(B) 지점을 검토하라. 샤드 장애·지연 시 결과가 조용히 틀리거나
//       집계가 깨질 수 있다. 어떤 상황에서 그런지 밝히고 고쳐라.

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

public interface IShardClient
{
    Task<ulong> QueryCurrencyTotalAsync();   // 이 샤드 재화 총량(네트워크). 실패 시 예외 가능
}

public sealed class EconomyMonitor
{
    private readonly IReadOnlyList<IShardClient> _shards;
    public EconomyMonitor(IReadOnlyList<IShardClient> shards) => _shards = shards;

    public async Task<ulong> GlobalCurrencyTotalAsync()
    {
        ulong total = 0;
        var tasks = new List<Task>();
        foreach (var sc in _shards)
        {
            tasks.Add(Task.Run(async () =>
            {
                try
                {
                    ulong part = await sc.QueryCurrencyTotalAsync();  // 타임아웃 없음
                    total += part;                                    // (B)
                }
                catch { /* 실패 샤드는 건너뜀 */ }
            }));
        }
        await Task.WhenAll(tasks);                                    // 전 샤드 대기
        return total;                                                // (A)
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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