28. 리샤딩 중 키→파티션 재매핑으로 인한 순서 보장 붕괴

난이도 최상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 키 기반 파티셔닝으로 "플레이어별 순서 보장" — C# 분산 처리
// - 플레이어 이벤트(장착/판매/강화 등)는 반드시 그 플레이어 단위로 발생 순서대로
//   처리되어야 한다(인과 순서). 예: "판매"가 "장착"보다 먼저 처리되면 안 됨.
// - 순서 보장을 위해 이벤트를 hash(playerId) % N 으로 N개의 파티션에 나누고,
//   각 파티션은 단일 소비자(single-threaded)가 FIFO 로 처리한다.
//   같은 플레이어의 이벤트는 항상 같은 파티션으로 가므로 순서가 지켜진다.
// - 부하가 늘면 파티션(소비자)을 추가해 스케일 아웃한다(리샤딩).
//
// 요구사항:
//   1) 같은 플레이어의 이벤트는 항상 발생 순서대로 처리되어야 한다.
//   2) 파티션을 추가(리샤딩)하는 동안에도 순서 보장이 깨지면 안 된다.
//
// 과제: 파티션을 추가한 직후, 일부 플레이어의 이벤트가 순서가 뒤바뀌거나 두
//       소비자에서 동시에 처리되는 일이 발생한다. (A),(B) 지점을 근거로 원인과
//       수정안을 설명하라.

using System;
using System.Collections.Generic;

public sealed class Event { public int PlayerId; public long Seq; public string Kind; }

public sealed class OrderedPartition
{
    public readonly int Index;
    public readonly Queue<Event> Queue = new Queue<Event>();  // 단일 소비자 FIFO
    public OrderedPartition(int index) { Index = index; }
}

public sealed class ShardedProcessor
{
    private readonly List<OrderedPartition> _partitions = new List<OrderedPartition>();
    private int _count;

    public ShardedProcessor(int n)
    {
        for (int i = 0; i < n; i++) _partitions.Add(new OrderedPartition(i));
        _count = n;
    }

    public void Route(Event e)
    {
        int idx = (int)((uint)e.PlayerId % (uint)_count);   // (A)
        _partitions[idx].Queue.Enqueue(e);
    }

    // 스케일 아웃: 파티션(소비자) 추가
    public void AddPartition()
    {
        var p = new OrderedPartition(_count);
        _partitions.Add(p);
        _count++;                                            // (B)
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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